home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / examples / demo / demosrc / d_roi.pro < prev    next >
Text File  |  1997-07-08  |  78KB  |  2,283 lines

  1. PRO DemoROI$Read_Image, File, TrueColorImage, ColorTable
  2. ;
  3. ; Modify this routine to read specific image types other
  4. ; than JPEG.
  5. ;
  6. READ_JPEG, File, TrueColorImage, /TRUE
  7. END
  8.  
  9.  
  10. PRO DemoROI$Read_File_And_Initialize, pDemo$State, File = File
  11. ;
  12. ; This routine reads the image file (expected to be a
  13. ; JPEG) and initializes the environment.
  14. ;
  15. IF (N_ELEMENTS(File) EQ 0) THEN BEGIN
  16.    File = DIALOG_PICKFILE(Filter = "*.jpg")
  17.    IF (File EQ '') THEN BEGIN
  18.       RETURN
  19.    ENDIF
  20. ENDIF
  21. WIDGET_CONTROL, /HOURGLASS
  22. DemoROI$Read_Image, File, TrueColorImage
  23. ;
  24. ; If the image red was not a three-level TrueColor
  25. ; image, then stack the image.  (This may need
  26. ; some more work.)
  27. ;
  28. SizeOfTrueColor = SIZE(TrueColorImage)
  29. IF (SizeOfTrueColor[0] NE 3) THEN BEGIN
  30.    TC = BYTARR(3, SizeOfTrueColor[1], SizeOfTrueColor[2])
  31.    FOR I = 0, 2 DO BEGIN
  32.       TC[I, *, *] = TrueColorImage
  33.    ENDFOR
  34.    TrueColorImage = TC
  35. ENDIF
  36. Demo$State = *pDemo$State
  37. ;
  38. ; If specified, rebin the image to fit the
  39. ; maximum window size.
  40. ;
  41. SizeOfImage = SIZE(TrueColorImage)
  42. IF (Demo$State.ScaleImagesToFit) THEN BEGIN
  43.    IF ((SizeOfImage[2] GT Demo$State.MaxWindowDimension) OR $
  44.        (SizeOfImage[3] GT Demo$State.MaxWindowDimension)) THEN BEGIN
  45.       Scale = MAX([SizeOfImage[2]/$
  46.          FLOAT(Demo$State.MaxWindowDimension), SizeOfImage[3]/ $
  47.          FLOAT(Demo$State.MaxWindowDimension)])
  48.       TrueColorImage = CONGRID(TrueColorImage, 3, $
  49.          SizeOfImage[2]/Scale, SizeOfImage[3]/Scale)
  50.       Demo$State.SizeOfImage = SIZE(TrueColorImage)
  51.    ENDIF ELSE BEGIN
  52.       Demo$State.SizeOfImage = SizeOfImage
  53.    ENDELSE
  54. ENDIF ELSE BEGIN
  55.    Demo$State.SizeOfImage = SizeOfImage
  56. ENDELSE
  57. XSize = Demo$State.SizeOfImage[2]
  58. YSize = Demo$State.SizeOfImage[3]
  59. ;
  60. ; Destroy the draw window and create a new
  61. ; one to fit the image.
  62. ;
  63. Widgets = TEMPORARY(*Demo$State.pWidgets)
  64. WIDGET_CONTROL, Widgets.DrawWidget, /DESTROY
  65. IF (Demo$State.ImageScrollbars) THEN BEGIN
  66.    IF ((Demo$State.MaxWindowDimension LT XSize) OR $
  67.        (Demo$State.MaxWindowDimension LT YSize)) THEN BEGIN
  68.       Widgets.DrawWidget = $
  69.          WIDGET_DRAW(Widgets.ImageBase, $
  70.          XSIZE = XSize, YSIZE = YSize, $
  71.          X_Scroll_Size = XSize < Demo$State.MaxWindowDimension, $
  72.          Y_Scroll_Size = YSize < Demo$State.MaxWindowDimension, $
  73.          RETAIN = 2)
  74.    ENDIF ELSE BEGIN
  75.       Widgets.DrawWidget = WIDGET_DRAW(Widgets.ImageBase, $
  76.          XSIZE = XSize, YSIZE = YSize, RETAIN = 2)
  77.    ENDELSE
  78. ENDIF ELSE BEGIN
  79.    Widgets.DrawWidget = WIDGET_DRAW(Widgets.ImageBase, $
  80.       XSIZE = XSize, YSIZE = YSize, RETAIN = 2)
  81. ENDELSE
  82. WIDGET_CONTROL, Widgets.DrawWidget, GET_VALUE = NewWindowID
  83. Demo$State.ImageWindow = NewWindowID
  84. ;
  85. ; Erase the draw window.
  86. ;
  87. WSET, Demo$State.ImageWindow
  88. ERASE, 0
  89. IF (Demo$State.HistoWindow NE 0) THEN BEGIN
  90.    WSET, Demo$State.HistoWindow
  91.    ERASE, 0
  92. ENDIF
  93. ;
  94. ; Create the new image and "ghost image" pixmaps.
  95. ;
  96. IF (Demo$State.ImagePixmap NE 0) THEN BEGIN
  97.    WDELETE, Demo$State.ImagePixmap
  98. ENDIF
  99. WINDOW, /PIXMAP, /FREE, XSIZE = XSize, YSIZE = YSize
  100. Demo$State.ImagePixmap = !D.WINDOW
  101. ERASE, 0
  102. IF (Demo$State.GrayBackgroundImagePixmap NE 0) THEN BEGIN
  103.    WDELETE, Demo$State.GrayBackgroundImagePixmap
  104. ENDIF
  105. WINDOW, /PIXMAP, /FREE, XSIZE = XSize, YSIZE = YSize
  106. Demo$State.GrayBackgroundImagePixmap = !D.WINDOW
  107. ;
  108. ; Display the image into the pixmap.
  109. ;
  110. WSET, Demo$State.ImagePixmap
  111. IF (!D.N_COLORS GT 256) THEN BEGIn
  112.    TV, TrueColorImage, TRUE = 1
  113.    RGBImage = 0B
  114. ENDIF ELSE BEGIN
  115.    RGBImage = COLOR_QUAN(TrueColorImage, 1, R, G, B, $
  116.       COLORS = !D.N_COLORS - Demo$State.NReservedColors - 1) + $
  117.       Demo$State.NReservedColors
  118.    Demo$State.BackgroundColor = MIN(R + G + B)
  119.    Gray = INDGEN(Demo$State.NReservedColors) * 20
  120.    TVLCT, [Gray, R, 255], [Gray, G, 255], [Gray, B, 255]
  121.    TV, RGBImage
  122. ENDELSE
  123. ;
  124. ; Initialize the flags, buttons, and widgets.
  125. ;
  126. Demo$State.Initialized = 0B
  127. Demo$State.GhostBackground = 0
  128. Demo$State.ManualCenter = [0., 0.]
  129. Demo$State.PreviousCenter = [0., 0.]
  130. WIDGET_CONTROL, Widgets.DrawWidget, $
  131.    DRAW_BUTTON_EVENTS = Demo$State.ManualMode
  132. WIDGET_CONTROL, Widgets.GhostOnButton2, SET_BUTTON = 0
  133. WIDGET_CONTROL, Widgets.GhostOffButton2, /SET_BUTTON
  134. WIDGET_CONTROL, Widgets.GhostOnButton, SET_BUTTON = 0
  135. WIDGET_CONTROL, Widgets.GhostOffButton, /SET_BUTTON
  136. WIDGET_CONTROL, Widgets.GhostBase, SENSITIVE = 0
  137. ;
  138. ; Free up any existing object pointers.
  139. ;
  140. IF (PTR_VALID(Demo$State.pObjects)) THEN BEGIN
  141.    VPtr = PTR_VALID(*Demo$State.pObjects)
  142.    OkayPtr = WHERE(VPtr NE 0, NOkayPtr)
  143.    IF (NOkayPtr NE 0) THEN BEGIN
  144.       PTR_FREE, (*Demo$State.pObjects)[OkayPtr]
  145.    ENDIF
  146.    PTR_FREE, Demo$State.pObjects
  147. ENDIF
  148. ;
  149. ; Save the image information into the state
  150. ; structure.
  151. ;
  152. *Demo$State.pTrueColorImage = TEMPORARY(TrueColorImage)
  153. *Demo$State.pRGBImage = TEMPORARY(RGBImage)
  154. *Demo$State.pWidgets = TEMPORARY(Widgets)
  155. *pDemo$State = TEMPORARY(Demo$State)
  156. ;
  157. ; Continue processing.
  158. ;
  159. DemoROI$Generate_Gray_Background, pDemo$State
  160. DemoROI$Weight_Image_By_Layer, pDemo$State
  161. DemoROI$Display_Original_Image, pDemo$State
  162. END
  163.  
  164.  
  165. FUNCTION DemoROI$Histogram, pDemo$State, SurfaceBrightness
  166. ;
  167. ; This routine calculates the distribution of surface
  168. ; brightness among the objects.  Surface brightness
  169. ; is defined as the sum of the pixels in the R, G,
  170. ; and B planes divided by the area of the object.
  171. ;
  172. Status = 1
  173. Demo$State = *pDemo$State
  174. IF (Demo$State.DemoMode) THEN BEGIN
  175.    Widgets = TEMPORARY(*Demo$State.pWidgets)
  176.    WIDGET_CONTROL, Widgets.AstroStatusLabel, SET_VALUE = $
  177.       'Calculating brightness distribution...'
  178.    *Demo$State.pWidgets = TEMPORARY(Widgets)
  179. ENDIF
  180. ;
  181. ; Just use IDL's HISTOGRAM function.
  182. ;
  183. Histo = HISTOGRAM(SurfaceBrightness, REVERSE_INDICES = R, $
  184.    MAX = 765, MIN = 0)
  185. ;
  186. ; Display the histogram, if specified.
  187. ;
  188. IF (Demo$State.HistoWindow NE 0) THEN BEGIN
  189.    WSET, Demo$State.HistoWindow
  190.    PLOT, Histo, XSTYLE = 4, YSTYLE = 4, YMARGIN = [0, 0], $
  191.       XMARGIN = [0, 0], COLOR = Demo$State.ColorWhite
  192.    XYOUTS, .5, .9, 'Max Cts/Pixel = ' + STRTRIM(N_ELEMENTS(Histo), 2), $
  193.       /NORMAL, ALIGNMENT = .5
  194. ENDIF
  195. ;
  196. ; The selection criteria may be too tight.
  197. ;
  198. NHisto = N_ELEMENTS(Histo)
  199. IF (NHisto LT Demo$State.MinBrightness) THEN BEGIN
  200.    v = DIALOG_MESSAGE('Minimum brightness value is set too low.' + $
  201.       '  The minimum data value is ' + $
  202.       STRTRIM(NHisto, 2) + ' counts per pixel.')
  203.    Status = 0
  204. ENDIF
  205. ;
  206. ; Save the histogram data.
  207. ;
  208. *Demo$State.pHistogram = {Histogram : Histo, ReverseIndices : R}
  209. *pDemo$State = TEMPORARY(Demo$State)
  210. RETURN, Status
  211. END
  212.  
  213.  
  214. PRO DemoROI$Display_Chosen_Subset, pDemo$State, $
  215.    Hide_First_Pass = Hide_First_Pass, No_Histogram = No_Histogram
  216. ;
  217. ; This routine displays the chosen objects from the image.
  218. ;
  219. Demo$State = *pDemo$State
  220. RGBImage = TEMPORARY(*Demo$State.pRGBImage)
  221. TrueColorImage = TEMPORARY(*Demo$State.pTrueColorImage)
  222. pObjects = TEMPORARY(*Demo$State.pObjects)
  223. WSET, Demo$State.ImageWindow
  224. IF (NOT KEYWORD_SET(Hide_First_Pass)) THEN BEGIN
  225. ;
  226. ; The visual sort is just for effect.  No real processing
  227. ; is done here.  The objects are displayed according
  228. ; to their ranking in the surface brightness distribution
  229. ; with fainter objects displayed first.
  230. ;
  231.    IF (Demo$State.DemoMode) THEN BEGIN
  232.       Widgets = TEMPORARY(*Demo$State.pWidgets)
  233.       WIDGET_CONTROL, Widgets.AstroStatusLabel, SET_VALUE = $
  234.          'Sorting by surface brightness...'
  235.       *Demo$State.pWidgets = TEMPORARY(Widgets)
  236.    ENDIF
  237.    Histogram = TEMPORARY(*Demo$State.pHistogram)
  238.    IF (!D.N_COLORS GT 256) THEN BEGIN
  239. ;
  240. ; TrueColor display.
  241. ;
  242.       FOR I = Demo$State.MinBrightness, $
  243.          MIN([N_ELEMENTS(Histogram.Histogram) - 1, $
  244.          Demo$State.MaxBrightness]) DO BEGIN
  245.          IF (Histogram.ReverseIndices[I] NE $
  246.             Histogram.ReverseIndices[I + 1]) THEN BEGIN
  247.             These = Histogram.ReverseIndices[ $
  248.                Histogram.ReverseIndices[I] : $
  249.                Histogram.ReverseIndices[I + 1] - 1]
  250.             DEVICE, COPY = [0, 0, Demo$State.SizeOfImage[2], $
  251.                   Demo$State.SizeOfImage[3], 0, 0, $
  252.                   Demo$State.GrayBackgroundImagePixmap]
  253.             WSET, Demo$State.ImageWindow
  254.             FOR J = 0L, N_ELEMENTS(These) - 1 DO BEGIN
  255.                Object = TEMPORARY(*pObjects[These[J]])
  256.                TV, TrueColorImage[*, $
  257.                   Object.Origin[0]:Object.Origin[0] + $
  258.                   Object.Extents[0] - 1, $
  259.                   Object.Origin[1]:Object.Origin[1] + $
  260.                   Object.Extents[1] - 1],$
  261.                   MIN(Object.Perimeter[*, 0]), $
  262.                   MIN(Object.Perimeter[*, 1]), TRUE = 1
  263.                *pObjects[These[J]] = TEMPORARY(Object)
  264.             ENDFOR
  265.          ENDIF
  266.       ENDFOR
  267.    ENDIF ELSE BEGIN
  268. ;
  269. ; 256 color mode.
  270. ;
  271.       FOR I = Demo$State.MinBrightness, $
  272.          MIN([N_ELEMENTS(Histogram.Histogram) - 1, $
  273.          Demo$State.MaxBrightness]) DO BEGIN
  274.          IF (Histogram.ReverseIndices[I] NE $
  275.             Histogram.ReverseIndices[I + 1]) THEN BEGIN
  276.             These = Histogram.ReverseIndices[ $
  277.                Histogram.ReverseIndices[I] : $
  278.                Histogram.ReverseIndices[I + 1] - 1]
  279.             DEVICE, COPY = [0, 0, Demo$State.SizeOfImage[2], $
  280.                   Demo$State.SizeOfImage[3], 0, 0, $
  281.                   Demo$State.GrayBackgroundImagePixmap]
  282.             WSET, Demo$State.ImageWindow
  283.             FOR J = 0L, N_ELEMENTS(These) - 1 DO BEGIN
  284.                Object = TEMPORARY(*pObjects[These[J]])
  285.                TV, RGBImage[Object.Origin[0]:Object.Origin[0] + $
  286.                   Object.Extents[0] - 1, $
  287.                   Object.Origin[1]:Object.Origin[1] + $
  288.                   Object.Extents[1] - 1], $
  289.                   MIN(Object.Perimeter[*, 0]), $
  290.                   MIN(Object.Perimeter[*, 1])
  291.                *pObjects[These[J]] = TEMPORARY(Object)
  292.             ENDFOR
  293.          ENDIF
  294.       ENDFOR
  295.    ENDELSE
  296.    *Demo$State.pHistogram = TEMPORARY(Histogram)
  297. ENDIF
  298. ;
  299. ; If ghosting, display the gray scale layer,
  300. ; otherwise erase to black.
  301. ;
  302. IF (Demo$State.GhostBackground) THEN BEGIN
  303.    DEVICE, COPY = [0, 0, Demo$State.SizeOfImage[2], $
  304.       Demo$State.SizeOfImage[3], 0, 0, $
  305.       Demo$State.GrayBackgroundImagePixmap]
  306. ENDIF ELSE BEGIN
  307.    ERASE, 0
  308. ENDELSE
  309. IF (Demo$State.DemoMode) THEN BEGIN
  310.    Widgets = TEMPORARY(*Demo$State.pWidgets)
  311.    WIDGET_CONTROL, Widgets.AstroStatusLabel, SET_VALUE = $
  312.       'Displaying objects...'
  313.    *Demo$State.pWidgets = TEMPORARY(Widgets)
  314. ENDIF
  315. IF (NOT KEYWORD_SET(No_Histogram)) THEN BEGIN
  316. ;
  317. ; Display objects in groups based on sorting
  318. ; order by surface brightness.
  319. ;
  320.    Histogram = TEMPORARY(*Demo$State.pHistogram)
  321.    IF (!D.N_COLORS GT 256) THEN BEGIN
  322. ;
  323. ; TrueColor mode.
  324. ;
  325.       FOR I = Demo$State.MinBrightness, $
  326.          MIN([N_ELEMENTS(Histogram.Histogram) - 1, $
  327.          Demo$State.MaxBrightness]) DO BEGIN
  328.          IF (Histogram.ReverseIndices[I] NE $
  329.             Histogram.ReverseIndices[I + 1]) THEN BEGIN
  330.             These = Histogram.ReverseIndices[ $
  331.                Histogram.ReverseIndices[I]: $
  332.                Histogram.ReverseIndices[I + 1] - 1]
  333.             FOR J = 0L, N_ELEMENTS(These) - 1 DO BEGIN
  334.                Object = TEMPORARY(*pObjects[These[J]])
  335.                GrayImage = TVRD(Object.Origin[0], Object.Origin[1], $
  336.                   Object.Extents[0], Object.Extents[1], TRUE = 1)
  337.                ColorSubImage = TrueColorImage[*, Object.Origin[0]: $
  338.                   Object.Origin[0] + Object.Extents[0] - 1, $
  339.                   Object.Origin[1]:Object.Origin[1] + $
  340.                   Object.Extents[1] - 1]
  341.                SubImagePixels = WHERE(Object.BiLevelMask NE 0)
  342.                FOR K = 0, 2 DO BEGIN
  343.                   G1 = REFORM(GrayImage[K, *, *])
  344.                   C1 = REFORM(ColorSubImage[K, *, *])
  345.                   G1[SubImagePixels] = C1[SubImagePixels]
  346.                   GrayImage[K, *, *] = G1
  347.                ENDFOR
  348.                TV, GrayImage, Object.Origin[0], Object.Origin[1], TRUE = 1
  349.                *pObjects[These[J]] = TEMPORARY(Object)
  350.             ENDFOR
  351.          ENDIF
  352.       ENDFOR
  353.    ENDIF ELSE BEGIN
  354. ;
  355. ; 256 color mode.
  356. ;
  357.       FOR I = Demo$State.MinBrightness, $
  358.          MIN([N_ELEMENTS(Histogram.Histogram) - 1, $
  359.          Demo$State.MaxBrightness]) DO BEGIN
  360.          IF (Histogram.ReverseIndices[I] NE $
  361.             Histogram.ReverseIndices[I + 1]) THEN BEGIN
  362.             These = Histogram.ReverseIndices[ $
  363.                Histogram.ReverseIndices[I]: $
  364.                Histogram.ReverseIndices[I + 1] - 1]
  365.             FOR J = 0L, N_ELEMENTS(These) - 1 DO BEGIN
  366.                Object = TEMPORARY(*pObjects[These[J]])
  367.                GrayImage = TVRD(Object.Origin[0], Object.Origin[1], $
  368.                   Object.Extents[0], Object.Extents[1])
  369.                ColorSubImage = RGBImage[Object.Origin[0]: $
  370.                   Object.Origin[0] + Object.Extents[0] - 1, $
  371.                   Object.Origin[1]:Object.Origin[1] + $
  372.                   Object.Extents[1] - 1]
  373.                SubImagePixels = WHERE(Object.BiLevelMask NE 0)
  374.                GrayImage[SubImagePixels] = ColorSubImage[SubImagePixels]
  375.                TV, GrayImage, Object.Origin[0], Object.Origin[1]
  376.                *pObjects[These[J]] = TEMPORARY(Object)
  377.             ENDFOR
  378.          ENDIF
  379.       ENDFOR
  380.    ENDELSE
  381.    *Demo$State.pHistogram = TEMPORARY(Histogram)
  382. ENDIF ELSE BEGIN
  383. ;
  384. ; In this mode we dispense with the sorting the objects
  385. ; by brightness and just display them
  386. ;
  387.    IF (!D.N_COLORS GT 256) THEN BEGIN
  388. ;
  389. ; TrueColor display.
  390. ;
  391.       FOR I = 0L, N_ELEMENTS(pObjects) - 1 DO BEGIN
  392.          Object = TEMPORARY(*pObjects[I])
  393.          IF ((!D.X_SIZE LT Object.Extents[0]) OR $
  394.              (!D.Y_SIZE LT Object.Extents[1])) THEN BEGIN
  395.             v = DIALOG_MESSAGE('problem with sizes', /info)
  396.          ENDIF
  397. ;
  398. ; Read the background that's currently displayed.
  399. ;
  400.          GrayImage = TVRD(Object.Origin[0], Object.Origin[1], $
  401.             Object.Extents[0], Object.Extents[1], TRUE = 1)
  402. ;
  403. ; Get the subimage of the TrueColor image that surrounds the
  404. ; object.
  405. ;
  406.          ColorSubImage = TrueColorImage[*, $
  407.             Object.Origin[0]:Object.Origin[0] + Object.Extents[0] - 1, $
  408.             Object.Origin[1]:Object.Origin[1] + Object.Extents[1] - 1]
  409. ;
  410. ; Determine which pixels in the subimage actually belong to the
  411. ; object.
  412. ;
  413.          SubImagePixels = WHERE(Object.BiLevelMask NE 0)
  414.          IF (N_ELEMENTS(SubImagePixels) NE Object.Extents[0]*Object.Extents[1]) THEN BEGIN
  415. ;
  416. ; Replace the pixels in the background subimage with the pixels
  417. ; belonging to the object.
  418. ;
  419.             Xs = SubImagePixels MOD Object.Extents[0]
  420.             Ys = SubImagePixels/Object.Extents[0]
  421.             FOR J = 0, 2 DO BEGIN
  422.                GrayImage[J, Xs, Ys] = ColorSubImage[J, Xs, Ys]
  423.             ENDFOR
  424.          ENDIF ELSE BEGIN
  425.             GrayImage = ColorSubImage
  426.          ENDELSE
  427. ;
  428. ; Put the modified subimage back into the window.
  429. ;
  430.          TV, GrayImage, Object.Origin[0], Object.Origin[1], TRUE = 1
  431.          *pObjects[I] = TEMPORARY(Object)
  432.       ENDFOR
  433.    ENDIF ELSE BEGIN
  434. ;
  435. ; 256 color mode.
  436. ;
  437.       FOR I = 0L, N_ELEMENTS(pObjects) - 1 DO BEGIN
  438.          Object = TEMPORARY(*pObjects[I])
  439.          IF ((!D.X_SIZE LT Object.Extents[0]) OR $
  440.              (!D.Y_SIZE LT Object.Extents[1])) THEN BEGIN
  441.             v = DIALOG_MESSAGE('problem with sizes', /info)
  442.          ENDIF
  443.          GrayImage = TVRD(Object.Origin[0], Object.Origin[1], $
  444.             Object.Extents[0], Object.Extents[1])
  445.          ColorSubImage = RGBImage[Object.Origin[0]:Object.Origin[0] + $
  446.             Object.Extents[0] - 1, $
  447.             Object.Origin[1]:Object.Origin[1] + Object.Extents[1] - 1]
  448.          SubImagePixels = WHERE(Object.BiLevelMask NE 0)
  449.          GrayImage[SubImagePixels] = ColorSubImage[SubImagePixels]
  450.          TV, GrayImage, Object.Origin[0], Object.Origin[1]
  451.          *pObjects[I] = TEMPORARY(Object)
  452.       ENDFOR
  453.    ENDELSE
  454. ENDELSE
  455. IF (Demo$State.DemoMode) THEN BEGIN
  456.    Widgets = TEMPORARY(*Demo$State.pWidgets)
  457.    WIDGET_CONTROL, Widgets.AstroStatusLabel, SET_VALUE = ''
  458.    *Demo$State.pWidgets = TEMPORARY(Widgets)
  459. ENDIF
  460. ;
  461. ; Store the objects and images back into the state structure.
  462. ;
  463. *Demo$State.pObjects = TEMPORARY(pObjects)
  464. *Demo$State.pRGBImage = TEMPORARY(RGBImage)
  465. *Demo$State.pTrueColorImage = TEMPORARY(TrueColorImage)
  466. *pDemo$State = TEMPORARY(Demo$State)
  467. END
  468.  
  469.  
  470. PRO DemoROI$Generate_Gray_Background, pDemo$State
  471. ;
  472. ; Thie routine creates the "ghost" background image.
  473. ; We've reserved a few indices at the bottom of the
  474. ; color table (in 256-color mode).
  475. ;
  476. Demo$State = *pDemo$State
  477. BWImage = BYTSCL(TOTAL(*Demo$State.pTrueColorImage, 1))
  478. Background = BYTSCL(BWImage, TOP = Demo$State.NReservedColors - 1)
  479. WSET, Demo$State.GrayBackgroundImagePixmap
  480. Background[WHERE(Background EQ 0)] = Demo$State.BackgroundColor
  481. IF (!D.N_COLORS GT 256) THEN BEGIN
  482. ;
  483. ; TrueColor mode.
  484. ;
  485.    TV, Background*20, CHANNEL = 0
  486. ENDIF ELSE BEGIN
  487. ;
  488. ; 256 color mode.
  489. ;
  490.    TV, Background
  491. ENDELSE
  492. *pDemo$State = TEMPORARY(Demo$State)
  493. END
  494.  
  495.  
  496. PRO DemoROI$Generate_Edge_Enhanced_Image, pDemo$State
  497. ;
  498. ; This routine generates the edge-enhanced image based
  499. ; on the black-and-white weighted image.  Here, we
  500. ; simply use SOBEL but you can use whatever technique
  501. ; is most appropriate.
  502. ;
  503. Demo$State = *pDemo$State
  504. IF (Demo$State.DemoMode) THEN BEGIN
  505.    Widgets = TEMPORARY(*Demo$State.pWidgets)
  506.    WIDGET_CONTROL, Widgets.AstroStatusLabel, SET_VALUE = $
  507.       'Generating edge-enhanced mask...'
  508.    *Demo$State.pWidgets = TEMPORARY(Widgets)
  509. ENDIF
  510. *Demo$State.pEdgeEnhancedImage = $
  511.     BYTSCL(SOBEL(*Demo$State.pBWWeightedImage))
  512. *pDemo$State = TEMPORARY(Demo$State)
  513. END
  514.  
  515.  
  516. PRO DemoROI$Weight_Image_By_Layer, pDemo$State
  517. ;
  518. ; This routine creates a black-and-white version
  519. ; of the original image, with specified weights
  520. ; applied to each layer.  The result is then scaled.
  521. ;
  522. Demo$State = *pDemo$State
  523. WeightedImage = *Demo$State.pTrueColorImage
  524. FOR I = 0, 2 DO BEGIN
  525.    WeightedImage[I, *, *] = WeightedImage[I, *, *] * $
  526.       Demo$State.Weights[I]
  527. ENDFOR
  528. BWWeightedImage = BYTSCL(TOTAL(WeightedImage, 1))
  529. IF (Demo$State.UseNegativeImage) THEN BEGIN
  530.    BWWeightedImage = 255 - TEMPORARY(BWWeightedImage)
  531. ENDIF
  532. *Demo$State.pBWWeightedImage = TEMPORARY(BWWeightedImage)
  533. *pDemo$State = TEMPORARY(Demo$State)
  534. ;
  535. ; Feed the result into the edge enhancement.
  536. ;
  537. DemoROI$Generate_Edge_Enhanced_Image, pDemo$State
  538. END
  539.  
  540.  
  541. PRO DemoROI$Display_Original_Image, pDemo$State
  542. ;
  543. ; This routine copies the original image pixmap to
  544. ; the display window.
  545. ;
  546. Demo$State = *pDemo$State
  547. WSET, Demo$State.ImageWindow
  548. DEVICE, COPY = [0, 0, Demo$State.SizeOfImage[2], $
  549.    Demo$State.SizeOfImage[3], 0, 0, Demo$State.ImagePixmap]
  550. *pDemo$State = TEMPORARY(Demo$State)
  551. END
  552.  
  553.  
  554. PRO DemoROI$Find_Edge_Enhanced_Contours, pDemo$State, $
  555.    PathInfo, Path_XY
  556. ;
  557. ; This routine takes as input the black and white image,
  558. ; filters out minimum and maximum thresholds from the
  559. ; edge enhancement, and contours the result to find
  560. ; individual "objects".
  561. ;
  562. Demo$State = *pDemo$State
  563. IF (Demo$State.DemoMode) THEN BEGIN
  564.    Widgets = TEMPORARY(*Demo$State.pWidgets)
  565.    WIDGET_CONTROL, Widgets.AstroStatusLabel, SET_VALUE = $
  566.       'Searching for objects...'
  567.    *Demo$State.pWidgets = TEMPORARY(Widgets)
  568. ENDIF
  569. EdgeEnhancedImage = TEMPORARY(*Demo$State.pEdgeEnhancedImage)
  570. BWWeightedImage = TEMPORARY(*Demo$State.pBWWeightedImage)
  571. CONTOUR, BWWeightedImage * (1 - $
  572.    ((EdgeEnhancedImage LT Demo$State.MinSobel) OR $
  573.    (EdgeEnhancedImage GT Demo$State.MaxSobel))), $
  574.    LEVEL =1, /FOLLOW, XMARGIN = [0, 0], YMARGIN = [0, 0], /NOERASE, $
  575.    PATH_INFO = PathInfo, PATH_XY = Path_XY, XSTYLE = 5, YSTYLE = 5
  576. ;
  577. ; PATH_XY and PATH_INFO define the outlines of the objects.
  578. ;
  579. IF (MAX(Path_XY[0, *]) GT 1.1) THEN BEGIN
  580.    Path_XY[0, *] = Path_XY[0, *]/!d.x_size
  581.    Path_XY[1, *] = Path_XY[1, *]/!d.y_size
  582. ENDIF
  583. *Demo$State.pBWWeightedImage = TEMPORARY(BWWeightedImage)
  584. *Demo$State.pEdgeEnhancedImage = TEMPORARY(EdgeEnhancedImage)
  585. *pDemo$State = TEMPORARY(Demo$State)
  586. END
  587.  
  588.  
  589. PRO DemoROI$Find_Objects, pDemo$State
  590. ;
  591. ; This routine finds the individual "objects" in
  592. ; an image and creates structures to define each
  593. ; one.
  594. ;
  595. DemoROI$Find_Edge_Enhanced_Contours, pDemo$State, PInfo, PXY
  596. Demo$State = *pDemo$State
  597. ;
  598. ; First, make sure we found some object perimeters that
  599. ; weren't filtered out by length criteria.
  600. ;
  601. IF (N_ELEMENTS(PInfo) NE 0) THEN BEGIN
  602.    IF (Demo$State.HighLowContours NE 2) THEN BEGIN
  603.       Low = WHERE((PInfo.High_Low EQ Demo$State.HighLowContours) $
  604.          AND (PInfo.N GE Demo$State.MinCircumference) AND $
  605.          (PInfo.N LE Demo$State.MaxCircumference), NLow)
  606.       ENDIF ELSE BEGIN
  607.          Low = WHERE((PInfo.N GE Demo$State.MinCircumference) AND $
  608.             (PInfo.N LE Demo$State.MaxCircumference), NLow)
  609.    ENDELSE
  610. ENDIF ELSE BEGIN
  611.    NLow = 0
  612. ENDELSE
  613. IF (NLow EQ 0) THEN BEGIN
  614.    StatsMessage = 'None found'
  615.    v = DIALOG_MESSAGE(StatsMessage, /Information)
  616.    *pDemo$State = TEMPORARY(Demo$State)
  617.    RETURN
  618. ENDIF
  619. ;
  620. ; Now we know we have some valid outlines.
  621. ; The subimage pixmap is used for temporary
  622. ; image manipulation.
  623. ;
  624. IF (Demo$State.SubImagePixmap NE 0) THEN BEGIN
  625.    WDELETE, Demo$State.SubImagePixmap
  626.    Demo$State.SubImagePixmap = 0
  627. ENDIF
  628. Widgets = TEMPORARY(*Demo$State.pWidgets)
  629. RGBImage = TEMPORARY(*Demo$State.pRGBImage)
  630. TrueColorImage = TEMPORARY(*Demo$State.pTrueColorImage)
  631. ;
  632. ; Since we've got objects, we allow the user to
  633. ; select "ghost" image display in the future.
  634. ;
  635. WIDGET_CONTROL, Widgets.GhostBase, SENSITIVE = 1
  636. ;
  637. ; Find the longest contour.
  638. ;
  639. Longest = MIN(WHERE(PInfo[Low].N EQ MAX(PInfo[Low].N)))
  640. SurfaceBrightness = FLTARR(N_ELEMENTS(Low))
  641. WSET, Demo$State.ImageWindow
  642. ;
  643. ; Allocate enough pointers to handle the individual
  644. ; objects, then loop over the contours.
  645. ;
  646. Pointers = PTRARR(N_ELEMENTS(Low), /ALLOCATE_HEAP)
  647. FOR I = 0L, N_ELEMENTS(Low) - 1 DO BEGIN
  648. ;
  649. ; Get the individual outlines in device coordinates.
  650. ; Make sure they're clipped to it into the physical
  651. ; size of the image.
  652. ;
  653.    S = [LINDGEN(PInfo[Low[I]].N), 0]
  654.    ThisXY = REFORM(PXY[*, PInfo[Low[I]].Offset + S])
  655.    ThisXY[0, *] = ThisXY[0, *] * Demo$State.SizeOfImage[2] < $
  656.       (Demo$State.SizeOfImage[2] - 1)
  657.    ThisXY[1, *] = ThisXY[1, *] * Demo$State.SizeOfImage[3] < $
  658.       (Demo$State.SizeOfImage[3] - 1)
  659. ;
  660. ; Get the maximum extents of the objects.
  661. ;
  662.    X = LONG(REFORM(ThisXY[0, *]))
  663.    Y = LONG(REFORM(ThisXY[1, *]))
  664.    DeltaX = (MAX(X) - MIN(X) + 1)
  665.    DeltaY = (MAX(Y) - MIN(Y) + 1)
  666. ;
  667. ; Plot the contour of the object on the image.
  668. ;
  669.    WSET, Demo$State.ImageWindow
  670.    PLOTS, ThisXY, /DEVICE, COLOR = Demo$State.ColorWhite
  671. ;
  672. ; Create a temporary pixmap large enough to hold the
  673. ; object.  If we need a larger pixmap, than already
  674. ; exists, we create it.  If we use small pixmaps,
  675. ; we can speed processing.
  676. ;
  677.    IF (Demo$State.SubImagePixmap EQ 0) THEN BEGIN
  678.       WINDOW, /PIXMAP, /FREE, XSIZE = DeltaX, YSIZE = DeltaY, $
  679.          COLOR = 2
  680.       Demo$State.SubImagePixmap = !D.WINDOW
  681.    ENDIF ELSE BEGIN
  682.       WSET, Demo$State.SubImagePixmap
  683.       IF ((!D.X_SIZE LT DeltaX) OR (!D.Y_SIZE LT DeltaY)) $
  684.       THEN BEGIN
  685.          WDELETE, Demo$State.SubImagePixmap
  686.          WINDOW, /FREE, /PIXMAP, XSIZE = DeltaX, YSIZE = DeltaY, $
  687.             COLOR = 2
  688.          Demo$State.SubImagePixmap = !D.WINDOW
  689.       ENDIF
  690.    ENDELSE
  691. ;
  692. ; Erase the pixmap to 0.
  693. ;
  694.    ERASE, 0
  695.    IF (!D.N_COLORS GT 256) THEN BEGIN
  696.       SubImage = Total(TrueColorImage[*, MIN(X):MAX(X), $
  697.          MIN(Y):MAX(Y)], 1)
  698.    ENDIF ELSE BEGIN
  699.       SubImage = RGBImage[MIN(X):MAX(X), MIN(Y):MAX(Y)]
  700.    ENDELSE
  701. ;
  702. ; Fill the interior of the contour with 255s.  This creates
  703. ; a bilevel image mask.  We would use 1s, but in 65535 color
  704. ; mode, the 1 is rounded down to 0.
  705. ;
  706.    POLYFILL, X - MIN(X), Y - MIN(Y), Color = 255, /DEVICE
  707. ;
  708. ; Here's where we convert to 0s and 1s only.
  709. ;
  710.    BiLevel =  TVRD() < 1
  711. ;
  712. ; Apply the bilevel mask to the original image data to
  713. ; zero out regions outside the contour.
  714. ;
  715.    SubImage = TEMPORARY(SubImage) * BiLevel[0:DeltaX - 1, $
  716.       0:DeltaY - 1]
  717. ;
  718. ; Calculate the brightness of each layer of the
  719. ; masked subimage.
  720. ;
  721.    FOR J = 0, 2 DO BEGIN
  722.       SurfaceBrightness[I] = SurfaceBrightness[I] + $
  723.          TOTAL(REFORM(TrueColorImage[J, MIN(X):MAX(X), $
  724.          MIN(Y):MAX(Y)])*BiLevel[0:DeltaX - 1, 0:DeltaY - 1])
  725.    ENDFOR
  726. ;
  727. ; Determine the surface brightness by dividing by the
  728. ; area of the object.
  729. ;
  730.    Area = POLY_AREA(X, Y)
  731.    SurfaceBrightness[I] = SurfaceBrightness[I]/Area
  732. ;
  733. ; Save the information about this object into a
  734. ; structure.
  735. ;
  736.    *Pointers[I] = { $
  737.       Origin : [MIN(X), MIN(Y)], $
  738.       Extents : [DeltaX, DeltaY], $
  739.       Perimeter : [[X], [Y]], $
  740.       Area : Area, $
  741.       BiLevelMask : BiLevel[0:DeltaX - 1, 0:DeltaY - 1], $
  742.       SurfaceBrightness : SurfaceBrightness[I] $
  743.       }
  744. ENDFOR
  745. ;
  746. ; Demo$State.pObjects is a pointer to the array of pointers
  747. ; to objects.
  748. ;
  749. IF (NOT PTR_VALID(Demo$State.pObjects)) THEN BEGIN
  750.    Demo$State.pObjects = PTR_NEW(/ALLOCATE_HEAP)
  751. ENDIF ELSE BEGIN
  752. ;
  753. ; Free up any existing pointers to objects.
  754. ;
  755.    Ptrs = PTR_VALID(*Demo$State.pObjects)
  756.    UsedPtrs = Where(Ptrs NE 0, NUsedPtrs)
  757.    IF (NUsedPtrs NE 0) THEN BEGIN
  758.       PTR_FREE, (*Demo$State.pObjects)[UsedPtrs]
  759.    ENDIF
  760. ENDELSE
  761. ;
  762. ; Free up the temporary pixmap.
  763. ;
  764. IF (Demo$State.SubImagePixmap NE 0) THEN BEGIN
  765.    WDELETE, Demo$State.SubImagePixmap
  766.    Demo$State.SubImagePixmap = 0
  767. ENDIF
  768. ;
  769. ; Save the images and objects to the state structure.
  770. ;
  771. *Demo$State.pTrueColorImage = TEMPORARY(TrueColorImage)
  772. *Demo$State.pRGBImage = TEMPORARY(RGBImage)
  773. *Demo$State.pWidgets = TEMPORARY(Widgets)
  774. *Demo$State.pObjects = TEMPORARY(Pointers)
  775. *pDemo$State = TEMPORARY(Demo$State)
  776. ;
  777. ; If there are objects not filtered out by the
  778. ; surface brightness limits imposed by the user,
  779. ; display them.
  780. ;
  781. IF (DemoROI$Histogram(pDemo$State, SurfaceBrightness)) $
  782. THEN BEGIN
  783.    DemoROI$Display_Chosen_Subset, pDemo$State
  784. ENDIF
  785. END
  786.  
  787.  
  788. PRO DemoROI$Ghost_Image_Event, Event
  789. ;
  790. ; This routine takes ghost button on/off
  791. ; events and redisplays any objects.
  792. ;
  793. WIDGET_CONTROL, /HOURGLASS
  794. WIDGET_CONTROL, Event.Top, GET_UVALUE = pDemo$State
  795. WIDGET_CONTROL, Event.ID, GET_VALUE = ButtonValue
  796. (*pDemo$State).GhostBackground = ButtonValue EQ 'On'
  797. IsManual = (*pDemo$State).ManualMode
  798. IF (PTR_VALID((*pDemo$State).pObjects)) THEN BEGIN
  799.    DemoROI$Display_Chosen_Subset, pDemo$State, /Hide_First_Pass, $
  800.       No_Histogram = IsManual
  801. ENDIF
  802. WIDGET_CONTROL, Event.Top, /CLEAR_EVENTS
  803. END
  804.  
  805.  
  806. PRO DemoROI$Apply_Selection, pDemo$State, Show = Show
  807. ;
  808. ; This routine determines the current settings of the
  809. ; control widgets and creates or displays objects based
  810. ; on changes from the previous state.
  811. ;
  812. Demo$State = *pDemo$State
  813. Widgets = TEMPORARY(*Demo$State.pWidgets)
  814. IF (Demo$State.DemoMode) THEN BEGIN
  815.    WIDGET_CONTROL, Widgets.AstroStatusLabel, SET_VALUE = $
  816.       'Applying new selection criteria...'
  817. ENDIF
  818. ;
  819. ; Get the values of the various control widgets.
  820. ;
  821. WIDGET_CONTROL, Widgets.SobelSlider, GET_VALUE = MinSobel
  822. WIDGET_CONTROL, Widgets.MinCircumferenceSlider, $
  823.    GET_VALUE = MinCircumference
  824. WIDGET_CONTROL, Widgets.MaxCircumferenceSlider, $
  825.    GET_VALUE = MaxCircumference
  826. WIDGET_CONTROL, Widgets.RedSlider, GET_VALUE = RedWeight
  827. WIDGET_CONTROL, Widgets.GreenSlider, GET_VALUE = GreenWeight
  828. WIDGET_CONTROL, Widgets.BlueSlider, GET_VALUE = BlueWeight
  829. WIDGET_CONTROL, Widgets.MinBrightnessSlider, $
  830.    GET_VALUE = MinBrightness
  831. WIDGET_CONTROL, Widgets.MaxBrightnessSlider, $
  832.    GET_VALUE = MaxBrightness
  833. *Demo$State.pWidgets = TEMPORARY(Widgets)
  834. ;
  835. ; See if any of the values have changed.
  836. ;
  837. IF ((((Demo$State.MinSobel EQ MinSobel) AND $
  838.      (Demo$State.MinCircumference EQ MinCircumference) AND $
  839.      (Demo$State.MaxCircumference EQ MaxCircumference) AND $
  840.      (TOTAL(Abs(Demo$State.Weights - $
  841.       [RedWeight, GreenWeight, BlueWeight])) EQ 0) AND $
  842.      (Demo$State.Initialized))) AND $
  843.      (NOT Demo$State.ModifiedHighLow)) THEN BEGIN
  844. ;
  845. ; No changes were made to parameters that require searching
  846. ; for a new set of objects.  Brightness filtering is performed
  847. ; after objects are found.
  848. ;
  849.    Demo$State.MinBrightness = MinBrightness
  850.    Demo$State.MaxBrightness = MaxBrightness
  851.    *pDemo$State = TEMPORARY(Demo$State)
  852.    DemoROI$Display_Chosen_Subset, pDemo$State, /Hide_First_Pass
  853. ENDIF ELSE BEGIN
  854. ;
  855. ; At least one parameter changed that will require us to generate
  856. ; a new object list.
  857. ;
  858.    Demo$State.MinSobel = MinSobel
  859.    Demo$State.MinCircumference = MinCircumference
  860.    Demo$State.MaxCircumference = MaxCircumference
  861.    Demo$State.Weights = [RedWeight, GreenWeight, BlueWeight]
  862.    Demo$State.MinBrightness = MinBrightness
  863.    Demo$State.MaxBrightness = MaxBrightness
  864.    Demo$State.Initialized = 1B
  865.    Demo$State.ModifiedHighLow = 0
  866.    *pDemo$State = TEMPORARY(Demo$State)
  867.    DemoROI$Display_Original_Image, pDemo$State
  868.    DemoROI$Weight_Image_By_Layer, pDemo$State
  869.    DemoROI$Find_Objects, pDemo$State
  870. ENDELSE
  871. END
  872.  
  873.  
  874. PRO DemoROI$Main_Image_Event, Event, No_Event = No_Event
  875. ;
  876. ; This routine handles events from the "manual mode"
  877. ; of the demo.  Either the user has clicked the mouse
  878. ; on an object in order to have IDL find its outline,
  879. ; or a constructed event was sent (when the search pattern
  880. ; is tightened or loosened.)
  881. ;
  882. IF ((KEYWORD_SET(No_Event)) OR (Event.Press EQ 1)) THEN BEGIN
  883.    WIDGET_CONTROL, /HOURGLASS
  884.    WIDGET_CONTROL, Event.Top, GET_UVALUE = pDemo$State
  885.    Demo$State = *pDemo$State
  886.    BWWeightedImage = TEMPORARY(*Demo$State.pBWWeightedImage)
  887. ;
  888. ; Determine the "tightness" of the search about the value
  889. ; of the selected pixel.  The tightness is a threshold
  890. ; used by SEARCH2D.
  891. ;
  892.    WIDGET_CONTROL, (*Demo$State.pWidgets).TightnessSlider, $
  893.       GET_VALUE = Tightness
  894.    Tightness = 255 - TEMPORARY(Tightness)
  895. ;
  896. ; The mouse button event determines the origin about which
  897. ; SERACH2D should search for pixels "similar" to that one.
  898. ; Note that the black and white weighted image is used
  899. ; so the search takes place in grayscale space rather than
  900. ; color space.
  901. ;
  902.    Demo$State.ManualCenter = [Event.X, Event.Y]
  903.    SubImage = SEARCH2D(BWWeightedImage, Event.X, Event.Y, $
  904.       BWWeightedImage[Event.X, Event.Y] - Tightness, $
  905.       BWWeightedImage[Event.X, Event.Y] + Tightness)
  906.    *Demo$State.pBWWeightedImage = TEMPORARY(BWWeightedImage)
  907. ;
  908. ; Make sure we found some pixels.
  909. ;
  910.    IF (N_ELEMENTS(SubImage) le 1) THEN BEGIN
  911.       *pDemo$State = TEMPORARY(Demo$State)
  912.       v = DIALOG_MESSAGE('Too few points.  ' + $
  913.          'Try a looser search pattern.')
  914.       RETURN
  915.    ENDIF
  916. ;
  917. ; Determine the outline and extents of the found
  918. ; object.
  919. ;
  920.    IndexY = SubImage/Demo$State.SizeOfImage[2]
  921.    IndexX = SubImage - (IndexY * Demo$State.SizeOfImage[2])
  922.    MinIndexX = MIN(IndexX)
  923.    MinIndexY = MIN(IndexY)
  924.    DeltaX = MAX(IndexX) - MinIndexX + 1
  925.    DeltaY = MAX(IndexY) - MinIndexY + 1
  926. ;
  927. ; Make sure the object extents are reasonable.
  928. ;
  929.    IF ((DeltaX LT 2) OR (DeltaY LT 2)) THEN BEGIN
  930.       *pDemo$State = TEMPORARY(Demo$State)
  931.       v = DIALOG_MESSAGE('Too few points.  ' + $
  932.          'Try a looser search pattern.')
  933.       RETURN
  934.    ENDIF
  935.    NewIndexX = IndexX - MIN(IndexX)
  936.    NewIndexY = IndexY - MIN(IndexY)
  937. ;
  938. ; Get the color subimage enclosed within the extents
  939. ; of the object.
  940. ;
  941.    IF (!D.N_COLORS LT 256) THEN BEGIN
  942.       RGBImage = TEMPORARY(*Demo$State.pRGBImage)
  943.       OriginalSubImage = RGBImage[MinIndexX:MinIndexX + $
  944.          DeltaX - 1, MinIndexY:MinIndexY + DeltaY - 1]
  945.    ENDIF ELSE BEGIN
  946.       TrueColorImage = TEMPORARY(*Demo$State.pTrueColorImage)
  947.       OriginalSubImage = REFORM(TrueColorImage[0, $
  948.          MinIndexX:MinIndexX + DeltaX - 1, MinIndexY:MinIndexY $
  949.          + DeltaY - 1])
  950.    ENDELSE
  951. ;
  952. ; Create a rectangular bilevel mask of 0s and 1s. 1s indicate
  953. ; pixels that are within the object.
  954. ;
  955.    BiLevel = OriginalSubImage*0
  956.    BiLevel2 = BiLevel
  957.    BiLevel[NewIndexX, NewIndexY] = 1
  958. ;
  959. ; A temporary pixmap is used to create the subimage
  960. ; to be displayed.
  961. ;
  962.    IF (Demo$State.SubImagePixmap NE 0) THEN BEGIN
  963.       WDELETE, Demo$State.SubImagePixmap
  964.    ENDIF
  965.    WINDOW, /FREE, /PIXMAP, XSIZE = DeltaX, YSIZE = DeltaY
  966.    Demo$State.SubImagePixmap = !D.WINDOW
  967. ;
  968. ; Contour the bilevel image to get the outline of the
  969. ; object.
  970. ;
  971.    CONTOUR, BiLevel, LEVEL =1, $
  972.       XMARGIN = [0, 0], YMARGIN = [0, 0], /NOERASE, /FOLLOW, $
  973.       PATH_INFO = PInfo, PATH_XY = PXY, XSTYLE = 5, YSTYLE = 5, $
  974.       /DEVICE, XRANGE = [0, DeltaX - 1], YRANGE = [0, DeltaY - 1]
  975.    IF (MAX(PXY) GT 1.01) THEN BEGIN
  976.       PXY[0, *] = PXY[0, *]/!d.x_vsize
  977.       PXY[1, *] = PXY[1, *]/!d.y_vsize
  978.    ENDIF
  979.    WSET, Demo$State.ImageWindow
  980.    GaveUp = 0B
  981.    IF (N_ELEMENTS(PInfo) GT 1) THEN BEGIN
  982.       LongestPath = -1L
  983.       LongPath = -1L
  984.       I = -1L
  985. ;
  986. ; Sort the contours by length.  We want the longest
  987. ; possible contour that contains the object.
  988. ;
  989.       LengthSort = REVERSE(SORT(PInfo.N))
  990.       WHILE ((NOT GaveUp) AND (LongestPath EQ -1) AND $
  991.          (I LT N_ELEMENTS(PInfo) - 1)) DO BEGIN
  992.          I = I + 1L
  993.          LongPath = LengthSort[I]
  994.          BiLevel2[*] = 0B
  995.          S = [LINDGEN(PInfo[LongPath].N), 0]
  996.          ThisXY = REFORM(PXY[*, PInfo[LongPath].Offset + S])
  997.          ThisXY[0, *] = ThisXY[0, *] * DeltaX
  998.          ThisXY[1, *] = ThisXY[1, *] * DeltaY
  999.          X = LONG(REFORM(ThisXY[0, *]))
  1000.          Y = LONG(REFORM(ThisXY[1, *]))
  1001.          DEVICE, COPY = [0, 0, Demo$State.SizeOfImage[2], $
  1002.             Demo$State.SizeOfImage[3], 0, 0, $
  1003.             Demo$State.GrayBackgroundImagePixmap]
  1004. ;
  1005. ; Make sure the mouse click (or constructed event) was inside
  1006. ; this contour.
  1007. ;
  1008.          MinX = MIN(X)
  1009.          MaxX = MAX(X)
  1010.          MinY = MIN(Y)
  1011.          MaxY = MAX(Y)
  1012.          IF ((MaxX ge Event.X - MinIndexX) AND $
  1013.              (MaxY ge Event.Y - MinIndexY) AND $
  1014.              (MinX le Event.X - MinIndexX) AND $
  1015.              (MinY le Event.Y - MinIndexY)) THEN BEGIN
  1016. ;
  1017. ; We determine if a point is inside a polygon by filling
  1018. ; the polygon then setting the pixel to 1.  If the total
  1019. ; of the image is larger than it was without that pixel
  1020. ; set, then the pixel is outside the polygon.
  1021. ;
  1022.             Inside = POLYFILLV(X, Y, DeltaX, DeltaY)
  1023.             IF (Inside[0] NE -1) THEN BEGIN
  1024.                BiLevel2[Inside] = 1B
  1025.                ImageTotal = TOTAL(BiLevel2)
  1026.                BiLevel2[Event.X - MinIndexX, Event.Y - MinIndexY] = $
  1027.                   1B
  1028.                IF (ImageTotal EQ TOTAL(BiLevel2)) THEN BEGIN
  1029.                   IF (PInfo[LongPath].N GT LongestPath) THEN BEGIN
  1030.                      LongestPath = LongPath
  1031.                   ENDIF
  1032.                ENDIF
  1033.             ENDIF
  1034.          ENDIF
  1035. ;
  1036. ; Check in no more than 50 contours.  This will speed things up
  1037. ; if the search criteria are just too darned lax.
  1038. ;
  1039.          GaveUp = ((I GT 50) AND (LongestPath EQ -1))
  1040.       ENDWHILE
  1041.    ENDIF ELSE BEGIN
  1042.       LongestPath = 0
  1043.    ENDELSE
  1044.    IF (GaveUp) THEN BEGIN
  1045. ;
  1046. ; We didn't find an appropriate contour for this object.
  1047. ;
  1048.       IF (N_ELEMENTS(RGBImage) NE 0) THEN BEGIN
  1049.          *Demo$State.pRGBImage = TEMPORARY(RGBImage)
  1050.       ENDIF
  1051.       IF (N_ELEMENTS(TrueColorImage) NE 0) THEN BEGIN
  1052.          *Demo$State.pTrueColorImage = TEMPORARY(TrueColorImage)
  1053.       ENDIF
  1054.       *pDemo$State = TEMPORARY(Demo$State)
  1055.       v = DIALOG_MESSAGE('Too many contours.  ' + $
  1056.          'Try tightening the search pattern.')
  1057.       RETURN
  1058.    ENDIF
  1059. ;
  1060. ; The next thing we do is plot the contour around the object.
  1061. ;
  1062.    LongestPath = LongestPath > 0
  1063.    IF (N_ELEMENTS(RGBImage) NE 0) THEN BEGIN
  1064.       *Demo$State.pRGBImage = TEMPORARY(RGBImage)
  1065.    ENDIF
  1066.    S = [LINDGEN(PInfo[LongestPath].N), 0]
  1067.    ThisXY = REFORM(PXY[*, PInfo[LongestPath].Offset + S])
  1068.    ThisXY[0, *] = ThisXY[0, *] * DeltaX
  1069.    ThisXY[1, *] = ThisXY[1, *] * DeltaY
  1070.    X = LONG(REFORM(ThisXY[0, *])) + MIN(IndexX)
  1071.    Y = LONG(REFORM(ThisXY[1, *])) + MIN(IndexY)
  1072.    WSET, Demo$State.ImageWindow
  1073.    PLOTS, X, Y, /DEVICE, COLOR = Demo$State.ColorWhite
  1074. ;
  1075. ; Make sure the pointer array for the objects is
  1076. ; large enough to include this object.
  1077. ;
  1078.    NObjects = 0L
  1079.    Pointers = PTRARR(1)
  1080.    IF (PTR_VALID(Demo$State.pObjects)) THEN BEGIN
  1081.       NObjects = N_ELEMENTS(*Demo$State.pObjects)
  1082.       IF (NObjects NE 0) THEN BEGIN
  1083.          IF (TOTAL(ABS(Demo$State.PreviousCenter - $
  1084.             Demo$State.ManualCenter)) NE 0) THEN BEGIN
  1085.             Pointers = PTRARR(NObjects + 1)
  1086.             Pointers[0:NObjects - 1] = Temporary(*Demo$State.pObjects)
  1087.          ENDIF ELSE BEGIN
  1088.             Pointers = Temporary(*Demo$State.pObjects)
  1089.             NObjects = TEMPORARY(NObjects) - 1
  1090.             IF (PTR_VALID(Pointers[NObjects])) THEN BEGIN
  1091.                PTR_FREE, Pointers[NObjects]
  1092.             ENDIF
  1093.          ENDELSE
  1094.       ENDIF
  1095.    ENDIF
  1096. ;
  1097. ; Calculate the surface brightness of the object.
  1098. ;
  1099.    SurfaceBrightness = 0.
  1100.    IF (N_ELEMENTS(TrueColorImage) EQ 0) THEN BEGIN
  1101.       TrueColorImage = TEMPORARY(*Demo$State.pTrueColorImage)
  1102.    ENDIF
  1103.    FOR J = 0, 2 DO BEGIN
  1104.       SurfaceBrightness = TEMPORARY(SurfaceBrightness) + $
  1105.          TOTAL(REFORM(TrueColorImage[J, $
  1106.          MIN(IndexX):MAX(IndexX), $
  1107.          MIN(IndexY):MAX(IndexY)])* $
  1108.          BiLevel[0:DeltaX - 1, 0:DeltaY - 1])
  1109.    ENDFOR
  1110.    IF (N_ELEMENTS(TrueColorImage) NE 0) THEN BEGIN
  1111.       *Demo$State.pTrueColorImage = TEMPORARY(TrueColorImage)
  1112.    ENDIF
  1113.    Area = POLY_AREA(IndexX, IndexY)
  1114.    SurfaceBrightness = SurfaceBrightness/Area
  1115. ;
  1116. ; Create a structure for the characteristics of this object
  1117. ; and store it in an heap variable.
  1118. ;
  1119.    Pointers[NObjects] = PTR_NEW({ $
  1120.       Origin : [MIN(IndexX), MIN(IndexY)], $
  1121.       Extents : [DeltaX, DeltaY], $
  1122.       Perimeter : [[X], [Y]], $
  1123.       Area : Area, $
  1124.       BiLevelMask : BiLevel[0:DeltaX - 1, 0:DeltaY - 1], $
  1125.       SurfaceBrightness : SurfaceBrightness $
  1126.       })
  1127. ;
  1128. ; Save the pointers to the objects.
  1129. ;
  1130.    IF (NOT PTR_VALID(Demo$State.pObjects)) THEN BEGIN
  1131.       Demo$State.pObjects = PTR_NEW(Pointers, /NO_COPY)
  1132.    ENDIF ELSE BEGIN
  1133.       *Demo$State.pObjects = Temporary(Pointers)
  1134.    ENDELSE
  1135. ;
  1136. ; Put things back where they belong.
  1137. ;
  1138.    Demo$State.Initialized = 0
  1139.    Demo$State.PreviousCenter = Demo$State.ManualCenter
  1140.    IF (Demo$State.SubImagePixmap NE 0) THEN BEGIN
  1141.       WDELETE, Demo$State.SubImagePixmap
  1142.       Demo$State.SubImagePixmap = 0
  1143.    ENDIF
  1144.    IF (N_ELEMENTS(TrueColorImage) NE 0) THEN BEGIN
  1145.       *Demo$State.pTrueColorImage = TEMPORARY(TrueColorImage)
  1146.    ENDIF
  1147.    *pDemo$State = TEMPORARY(Demo$State)
  1148. ;
  1149. ; Display the objects in the list.
  1150. ;
  1151.    DemoROI$Display_Chosen_Subset, pDemo$State, /No_Histogram, $
  1152.       /Hide_First_Pass
  1153. ENDIF
  1154. WIDGET_CONTROL, Event.Top, /CLEAR_EVENTS
  1155. END
  1156.  
  1157.  
  1158. PRO DemoROI_Event, Event
  1159. ;
  1160. ; This routine is the main event handler for the demo.
  1161. ;
  1162. If (TAG_NAMES(Event, /STRUCTURE_NAME) EQ 'WIDGET_KILL_REQUEST') $
  1163.    THEN BEGIN
  1164.    WIDGET_CONTROL, event.top, /DESTROY
  1165. ;   v = DIALOG_MESSAGE('Use the File/Exit menu item to ' + $
  1166. ;      'close this application.')
  1167.    RETURN
  1168. ENDIF
  1169. WIDGET_CONTROL, /HOURGLASS
  1170. WIDGET_CONTROL, Event.Top, GET_UVALUE = pDemo$State
  1171. ;
  1172. ; This CATCH branch handles unexpected errors that might
  1173. ; arise in the event loop.  The cleanup of pointers,
  1174. ; pixmaps, etc. will occur upon the return to the
  1175. ; main procedure, after the base is destroyed.
  1176. ;
  1177. IF (NOT (*pDemo$State).NoCatch) THEN BEGIN
  1178.    ErrorStatus = 0
  1179.    CATCH, ErrorStatus
  1180.    IF (ErrorStatus NE 0) THEN BEGIN
  1181.       CATCH, /CANCEL
  1182.       v = DIALOG_MESSAGE(['Unexpected error in ROI:', $
  1183.          '!ERR_STRING = ' + !ERR_STRING, $
  1184.          '!SYSERROR = ' + STRTRIM(LONG(!SYSERROR), 2), $
  1185.          '!SYSERR_STRING = ', !SYSERR_STRING, $
  1186.          ' ', 'Cleaning up...'], /ERROR)
  1187.       WIDGET_CONTROL, Event.Top, /DESTROY
  1188.       RETURN
  1189.    ENDIF
  1190. ENDIF
  1191. ;
  1192. ; Get the value of the button that was selected and
  1193. ; act on it accordingly.
  1194. ;
  1195. WIDGET_CONTROL, Event.ID, GET_VALUE = ButtonValue
  1196. ButtonValue = STRTRIM(ButtonValue[0], 2)
  1197. CASE ButtonValue OF
  1198.    'Display Original Image' : DemoROI$Display_Original_Image, $
  1199.       pDemo$State
  1200.    'Quit' : WIDGET_CONTROL, Event.Top, /DESTROY
  1201.    'Open...' : BEGIN
  1202. ;
  1203. ; Open an image file.  This is not available in demo mode.
  1204. ;
  1205.       DemoROI$Read_File_And_Initialize, pDemo$State
  1206.       END
  1207.    'Astronomical Data' : BEGIN
  1208. ;
  1209. ; Demo modes require turning off or on various options.
  1210. ;
  1211.       DemoROI$Read_File_And_Initialize, pDemo$State, $
  1212.          FILE = FILEPATH('abell115.jpg', $
  1213.          SUBDIR = ['examples', 'demo', 'demodata'])
  1214.  
  1215.       Demo$State = *pDemo$State
  1216.       Widgets = TEMPORARY(*Demo$State.pWidgets)
  1217. ;
  1218. ; The astronomy demo is an "automatic mode" demo.
  1219. ;
  1220.       Demo$State.ManualMode = 0
  1221.       WIDGET_CONTROL, Widgets.ManualButton, SENSITIVE = 0
  1222.       WIDGET_CONTROL, Widgets.AutomaticButton, /SENSITIVE
  1223.       WIDGET_CONTROL, Widgets.DrawWidget, DRAW_BUTTON_EVENTS = 0
  1224.       WIDGET_CONTROL, Widgets.ControlsBase[0], MAP = 1
  1225.       FOR I = 1, N_ELEMENTS(Widgets.ControlsBase) - 1 DO BEGIN
  1226.          WIDGET_CONTROL, Widgets.ControlsBase[I], MAP = 0
  1227.       ENDFOR
  1228.       *Demo$State.pWidgets = TEMPORARY(Widgets)
  1229.       *pDemo$State = TEMPORARY(Demo$State)
  1230.       END
  1231.    'Medical Imaging Data' : BEGIN
  1232. ;
  1233. ; The medical imaging data is a "manual mode" demo.
  1234. ;
  1235.       DemoROI$Read_File_And_Initialize, pDemo$State, $
  1236.          FILE = FILEPATH('pollens.jpg', $
  1237.          SUBDIR = ['examples', 'demo', 'demodata'])
  1238.       Demo$State = *pDemo$State
  1239.       Widgets = TEMPORARY(*Demo$State.pWidgets)
  1240.       Demo$State.ManualMode = 1
  1241.       WIDGET_CONTROL, Widgets.ManualButton, /SENSITIVE
  1242.       WIDGET_CONTROL, Widgets.AutomaticButton, SENSITIVE = 0
  1243.       WIDGET_CONTROL, Widgets.DrawWidget, /DRAW_BUTTON_EVENTS
  1244.       WIDGET_CONTROL, Widgets.ControlsBase[1], MAP = 1
  1245.       WIDGET_CONTROL, Widgets.ControlsBase[0], MAP = 0
  1246.       FOR I = 2, N_ELEMENTS(Widgets.ControlsBase) - 1 DO BEGIN
  1247.          WIDGET_CONTROL, Widgets.ControlsBase[I], MAP = 0
  1248.       ENDFOR
  1249.       *Demo$State.pWidgets = TEMPORARY(Widgets)
  1250.       *pDemo$State = TEMPORARY(Demo$State)
  1251.       END
  1252.    'Start A New List Of Objects' : BEGIN
  1253. ;
  1254. ; In manual mode, this deletes the existing array of objects
  1255. ; and frees up their heap variables.
  1256. ;
  1257.       Demo$State = *pDemo$State
  1258.       IF (PTR_VALID(Demo$State.pObjects)) THEN BEGIN
  1259.          Ptrs = PTR_VALID(*Demo$State.pObjects)
  1260.          OkayPtrs = WHERE(Ptrs NE 0, NOkayPtrs)
  1261.          IF (NOkayPtrs NE 0) THEN BEGIN
  1262.             PTR_FREE, (*Demo$State.pObjects)[OkayPtrs]
  1263.          ENDIF
  1264.          PTR_FREE, Demo$State.pObjects
  1265.       ENDIF
  1266.       Demo$State.PreviousCenter = [0, 0]
  1267.       *pDemo$State = TEMPORARY(Demo$State)
  1268.       DemoROI$Display_Original_Image, pDemo$State
  1269.       END
  1270.    'High' : BEGIN
  1271. ;
  1272. ; Select "high" contours from the CONTOUR command
  1273. ; in automatic mode.  Not available in demo mode.
  1274. ;
  1275.       (*pDemo$State).HighLowContours = 1
  1276.       (*pDemo$State).ModifiedHighLow = 1
  1277.       END
  1278.    'Low' : BEGIN
  1279. ;
  1280. ; Select "low" contours from the CONTOUR command
  1281. ; in automatic mode.  Not available in demo mode.
  1282. ;
  1283.       (*pDemo$State).HighLowContours = 0
  1284.       (*pDemo$State).ModifiedHighLow = 1
  1285.       END
  1286.    'All' : BEGIN
  1287. ;
  1288. ; Select both "high" and "low" contours from the
  1289. ; CONTOUR command in automatic mode.  Not available
  1290. ; in demo mode.
  1291. ;
  1292.       (*pDemo$State).HighLowContours = 2
  1293.       (*pDemo$State).ModifiedHighLow = 1
  1294.       END
  1295.    'Find Objects' : BEGIN
  1296. ;
  1297. ; Apply the selection criteria to the image to find
  1298. ; the objects in automatic mode.
  1299. ;
  1300.       DemoROI$Apply_Selection, pDemo$State
  1301.       END
  1302.    'Apply Search Pattern' : BEGIN
  1303. ;
  1304. ; When manual mode is in effect, loosening or tightening
  1305. ; the search pattern and hitting "apply" will send
  1306. ; an event here.  We build a fake widget event and pass
  1307. ; it to the appropriate event handler as if the user
  1308. ; clicked on the image in the same place they clicked
  1309. ; before.
  1310. ;
  1311.       Center = (*pDemo$State).ManualCenter
  1312.       DemoROI$Main_Image_Event, {Press : 1, X : Center[0], $
  1313.          Y : Center[1], Top : Event.Top}, /No_Event
  1314.       END
  1315.    'Automatic' : BEGIN
  1316. ;
  1317. ; Change the object search mode to automatic.  This entails
  1318. ; hiding and revealing bases, among other things.
  1319. ;
  1320.       Demo$State = *pDemo$State
  1321.       Widgets = TEMPORARY(*Demo$State.pWidgets)
  1322.       Demo$State.ManualMode = 0
  1323.       WIDGET_CONTROL, Widgets.DrawWidget, DRAW_BUTTON_EVENTS = 0
  1324.       WIDGET_CONTROL, Widgets.ControlsBase[0], MAP = 1
  1325.       FOR I = 1, N_ELEMENTS(Widgets.ControlsBase) - 1 DO BEGIN
  1326.          WIDGET_CONTROL, Widgets.ControlsBase[I], MAP = 0
  1327.       ENDFOR
  1328.       *Demo$State.pWidgets = TEMPORARY(Widgets)
  1329.       *pDemo$State = TEMPORARY(Demo$State)
  1330.       END
  1331.    'Manual' : BEGIN
  1332. ;
  1333. ; Change the object search mode to manual.
  1334. ;
  1335.       Demo$State = *pDemo$State
  1336.       Widgets = TEMPORARY(*Demo$State.pWidgets)
  1337.       Demo$State.ManualMode = 1
  1338.       WIDGET_CONTROL, Widgets.DrawWidget, /DRAW_BUTTON_EVENTS
  1339.       WIDGET_CONTROL, Widgets.ControlsBase[1], MAP = 1
  1340.       WIDGET_CONTROL, Widgets.ControlsBase[0], MAP = 0
  1341.       FOR I = 2, N_ELEMENTS(Widgets.ControlsBase) - 1 DO BEGIN
  1342.          WIDGET_CONTROL, Widgets.ControlsBase[I], MAP = 0
  1343.       ENDFOR
  1344.       *Demo$State.pWidgets = TEMPORARY(Widgets)
  1345.       *pDemo$State = TEMPORARY(Demo$State)
  1346.       END
  1347.    'About the data...' : BEGIN
  1348. ;
  1349. ; Create a non-modal text widget with information about
  1350. ; the astronomical data.
  1351. ;
  1352.       IF (NOT XREGISTERED('AbellHelp')) THEN BEGIN
  1353.          Demo$State = *pDemo$State
  1354.          TextTLB = WIDGET_BASE(GROUP_LEADER = Event.Top, /COLUMN, $
  1355.             UVALUE = pDemo$State)
  1356.          IF (Demo$State.ManualMode) THEN BEGIN
  1357.          ENDIF ELSE BEGIN
  1358.             WIDGET_CONTROL, TextTLB, TLB_SET_TITLE = $
  1359.                'Abell 115 Image Data'
  1360.             T = WIDGET_TEXT(TextTLB, XSIZE = 60, YSIZE = 15, $
  1361.                VALUE = Demo$State.AstroDataText, /SCROLL)
  1362.             OkayButton = WIDGET_BUTTON(TextTLB, VALUE = 'OK')
  1363.             WIDGET_CONTROL, TextTLB, /REALIZE
  1364.          ENDELSE
  1365.          XMANAGER, 'AbellHelp', TextTLB, EVENT_HANDLER = $
  1366.             'DemoROI_Event', /NO_BLOCK
  1367.          *pDemo$State = TEMPORARY(Demo$State)
  1368.       ENDIF
  1369.       END
  1370.    'About the demo...' : BEGIN
  1371. ;
  1372. ; Create a non-modal text widget with information about
  1373. ; the code used in the demo, along with a shameless plug for
  1374. ; PSG.
  1375. ;
  1376.       IF (NOT XREGISTERED('DemoHelp')) THEN BEGIN
  1377.          Demo$State = *pDemo$State
  1378.          TextTLB = WIDGET_BASE(GROUP_LEADER = Event.Top, /COLUMN, $
  1379.             UVALUE = pDemo$State)
  1380.          IF (Demo$State.ManualMode) THEN BEGIN
  1381.          ENDIF ELSE BEGIN
  1382.             WIDGET_CONTROL, TextTLB, TLB_SET_TITLE = $
  1383.                'About the segmentation demo...'
  1384.             T = WIDGET_TEXT(TextTLB, XSIZE = 60, YSIZE = 15, $
  1385.                VALUE = Demo$State.AstroDemoText, /SCROLL)
  1386.             OkayButton = WIDGET_BUTTON(TextTLB, VALUE = 'OK')
  1387.             WIDGET_CONTROL, TextTLB, /REALIZE
  1388.          ENDELSE
  1389.          XMANAGER, 'DemoHelp', TextTLB, EVENT_HANDLER = $
  1390.             'DemoROI_Event', /NO_BLOCK
  1391.          *pDemo$State = TEMPORARY(Demo$State)
  1392.       ENDIF
  1393.       END
  1394.    'OK' : BEGIN
  1395. ;
  1396. ; This event is generated by the "OK" button on the "About..."
  1397. ; text widgets.
  1398. ;
  1399.       WIDGET_CONTROL, Event.Top, /DESTROY
  1400.       END
  1401.    Else :
  1402. ENDCASE
  1403. ;
  1404. ; Clear events.
  1405. ;
  1406. IF (ButtonValue NE 'Quit' and ButtonValue NE 'OK') THEN BEGIN
  1407.    WIDGET_CONTROL, Event.Top, /CLEAR_EVENTS
  1408. ENDIF
  1409. END
  1410.  
  1411.  
  1412. FUNCTION DemoROI$Free_Pointers, pDemo$State, $
  1413.    Keep_Objects = Keep_Objects
  1414. ;
  1415. ; This routine frees up all the heap variables associated with this
  1416. ; application that it can find.  If we're keeping objects (to be
  1417. ; returned to calling procedure), we don't free them.  The
  1418. ; return value is the array of object pointers, or a null pointer.
  1419. ;
  1420. IF (PTR_VALID(pDemo$State)) THEN BEGIN
  1421.    Demo$State = *pDemo$State
  1422.    IF (PTR_VALID(Demo$State.pObjects)) THEN BEGIN
  1423.       Ptrs = PTR_VALID(*Demo$State.pObjects)
  1424.       OkayPtrs = WHERE(Ptrs NE 0, NOkayPtrs)
  1425.       IF (NOkayPtrs NE 0) THEN BEGIN
  1426.          IF (NOT KEYWORD_SET(Keep_Objects)) THEN BEGIN
  1427.             PTR_FREE, (*Demo$State.pObjects)[OkayPtrs]
  1428.             ReturnPointers = PTR_NEW()
  1429.          ENDIF ELSE BEGIN
  1430.             ReturnPointers = (*Demo$State.pObjects)[OkayPtrs]
  1431.          ENDELSE
  1432.       ENDIF ELSE BEGIN
  1433.          ReturnPointers = PTR_NEW()
  1434.       ENDELSE
  1435.       ValidPtrs = PTR_VALID(*Demo$State.pObjects)
  1436.       Okay = WHERE(ValidPtrs NE 0, NOkay)
  1437.       IF ((NOkay NE 0) AND NOT KEYWORD_SET(Keep_Objects)) THEN BEGIN
  1438.          PTR_FREE, *Demo$State.pObjects
  1439.       ENDIF
  1440.    ENDIF ELSE BEGIN
  1441.       ReturnPointers = PTR_NEW()
  1442.    ENDELSE
  1443.    PTR_FREE, Demo$State.pWidgets
  1444.    PTR_FREE, Demo$State.pTrueColorImage
  1445.    PTR_FREE, Demo$State.pRGBImage
  1446.    PTR_FREE, Demo$State.pBWWeightedImage
  1447.    PTR_FREE, Demo$State.pEdgeEnhancedImage
  1448.    PTR_FREE, Demo$State.pHistogram
  1449.    PTR_FREE, Demo$State.pObjects
  1450.    PTR_FREE, pDemo$State
  1451. ENDIF ELSE BEGIN
  1452.    ReturnPointers = PTR_NEW()
  1453. ENDELSE
  1454. RETURN, ReturnPointers
  1455. END
  1456.  
  1457. PRO DemoROI_Cleanup, AppTLB
  1458.  
  1459. WIDGET_CONTROL, AppTLB, GET_UVALUE = pDemo$State
  1460.  
  1461. IF ((*pDemo$State).ImagePixmap NE 0) THEN BEGIN
  1462.    WDELETE, (*pDemo$State).ImagePixmap
  1463. ENDIF
  1464. IF ((*pDemo$State).GrayBackgroundImagePixmap NE 0) THEN BEGIN
  1465.    WDELETE, (*pDemo$State).GrayBackgroundImagePixmap
  1466. ENDIF
  1467. IF ((*pDemo$State).SubImagePixmap NE 0) THEN BEGIN
  1468.    WDELETE, (*pDemo$State).SubImagePixmap
  1469. ENDIF
  1470.  
  1471. ;
  1472. ; Restore the color table and system variables to their
  1473. ; values before the application was started.
  1474. ;
  1475. TVLCT, (*pDemo$State).OriginalR, $
  1476.        (*pDemo$State).OriginalG, $
  1477.        (*pDemo$State).OriginalB
  1478. !X = (*pDemo$State).XSave
  1479. !Y = (*pDemo$State).YSave
  1480. !Z = (*pDemo$State).ZSave
  1481. !P = (*pDemo$State).PSave
  1482.  
  1483. ; Free all the pointers associated with the application,
  1484. ; but keep any pointers to the objects.
  1485. ; Get some local variables out of the state structure first
  1486. ;
  1487. DemoMode = (*pDemo$State).DemoMode
  1488. No_Pointers = (*pDemo$State).No_Pointers
  1489. Group_Leader = (*pDemo$State).Group_Leader
  1490. pObjects = DemoROI$Free_Pointers(pDemo$State, /Keep_Objects)
  1491. ;
  1492. ; Free up the object pointers if we're running in a mode
  1493. ; where we don't want them to be returned.
  1494. ;
  1495. IF (DemoMode or No_Pointers) THEN BEGIN
  1496.    PTR_FREE, pObjects
  1497. ENDIF
  1498.  
  1499. ;
  1500. ;
  1501. if WIDGET_INFO(Group_Leader, /VALID_ID) then $
  1502.         WIDGET_CONTROL, Group_Leader, /MAP
  1503.  
  1504. END
  1505.  
  1506.  
  1507. Function DemoROI, Group_Leader = Group_Leader, $
  1508.     AppTLB = AppTLB, $
  1509.     No_Catch = No_Catch, No_Pointers = No_Pointers, $
  1510.     Astronomy_Demo = Astronomy_Demo, $
  1511.     Medical_Demo = Medical_Demo
  1512. ;+
  1513. ;  FILE:
  1514. ;       ROI.pro
  1515. ;
  1516. ;  CALLING SEQUENCE: p = DemoROI([, GROUP_LEADER = GROUP_LEADER $]
  1517. ;                             [, APPTLB = APPTLB $]
  1518. ;                             [, NO_CATCH = NO_CATCH $]
  1519. ;                             [, NO_POINTERS = NO_POINTERS $]
  1520. ;                             [, /ASTRONOMY_DEMO $]
  1521. ;                             [, /MEDICAL_DEMO])
  1522. ;
  1523. ;  PURPOSE:
  1524. ;       This application highlights the ability of IDL to find
  1525. ;       "objects" in an image.  It also shows implementation details
  1526. ;       of a fairly sophisticated widget application's management of
  1527. ;       heap variables, and displaying images in TrueColor vs. 256
  1528. ;       color modes in Direct Graphics.
  1529. ;
  1530. ;       This routine operates as a BLOCKING widget application due to
  1531. ;       its use of Direct Graphics IDL system variables and color
  1532. ;       tables.
  1533. ;
  1534. ;       The procedure was authored by the Professional Services Group
  1535. ;       of Research Systems.  The source code to the demonstration
  1536. ;       routine can be modified to return information about individual
  1537. ;       objects.  These data can in turn be fed into further
  1538. ;       processing and refinement steps.  For those desiring a larger
  1539. ;       implementation, the Professional Services Group can be
  1540. ;       contacted (psg@rsinc.com) for consulting and custom coding.
  1541. ;
  1542. ;  KEYWORD PARAMETERS:
  1543. ;       GROUP_LEADER can be set to the ID of a parent widget when
  1544. ;       this routine is called as a compound widget.
  1545. ;
  1546. ;       APPTLB returns the application top level base, mainly for
  1547. ;       use in the IDL Demo (i.e., don't worry about this if you
  1548. ;       aren't RSI.)
  1549. ;
  1550. ;       /NO_CATCH can be set for debugging purposes.  It will turn
  1551. ;       off automatic error trapping while the routine is running.
  1552. ;       It will also be necessary to turn off XMANAGER's error
  1553. ;       trapping before running the routine, via XMANAGER, CATCH = 0.
  1554. ;
  1555. ;       /NO_POINTERS can be set to prevent the application from
  1556. ;       returning the array of pointers to objects found in the
  1557. ;       image.  In demo mode, the default is to return a NULL pointer.
  1558. ;
  1559. ;       /ASTRONOMY_DEMO can be set to execute the procedure in
  1560. ;       "automatic" demo mode, with restricted access to tools.
  1561. ;
  1562. ;       /MEDICAL_DEMO can be set to execute the procedure in "manual"
  1563. ;       demo mode.
  1564. ;
  1565. ;  RETURN VALUE:
  1566. ;       In demo mode, the return value is a NULL pointer.  In
  1567. ;       application mode unless the /NO_POINTERS keyword is set, the
  1568. ;       return value is a an array of pointers which refer to heap
  1569. ;       variables that are anonymous structures containing
  1570. ;       characteristics of each object.  The anonymous structure is
  1571. ;       in the form:
  1572. ;
  1573. ;          {Origin : [X, Y],        The origin of the object's bounding
  1574. ;                                   rectangle with respect to the image
  1575. ;                                   origin, in pixels
  1576. ;           Extents : [DX, DY],     The X and Y lengths of the
  1577. ;                                   bounding rectangle, in pixels
  1578. ;           Perimeter : [[X], [Y]], The X and Y coordinates of the
  1579. ;                                   perimeter of the object within the
  1580. ;                                   bounding rectangle, in pixels with
  1581. ;                                   respect to Origin
  1582. ;           Area : Area,            The area in pixels of the object
  1583. ;                                   within the bounding perimeter
  1584. ;           BiLevelMask : Mask,     A byte array dimensioned [DX, DY]
  1585. ;                                   containing 1s for pixels within
  1586. ;                                   the bounding perimeter, and 0s for
  1587. ;                                   pixels outside it
  1588. ;           SurfaceBrightness : B}  The sum of the R, G, and B planes
  1589. ;                                   of the object, divided by the Area.
  1590. ;
  1591. ;  A typical call to display surface brightness distribution might
  1592. ;  look like:
  1593. ;
  1594. ;        ObjPtrs = DemoROI()
  1595. ;       SurfB = FltArr(N_elements(ObjPtrs))
  1596. ;       For I = 0, N_elements(ObjPtrs) - 1 Do $
  1597. ;          SurfB[I] = (*ObjPtrs[I]).SurfaceBrightness
  1598. ;       Plot, SurfB
  1599. ;       ; Don't forget to execute a "PTR_FREE, ObjPtrs" when you're
  1600. ;       ; finished!
  1601. ;
  1602. ;  MAJOR TOPICS: Visualization, Analysis, Demo, Language
  1603. ;
  1604. ;  CATEGORY:
  1605. ;       IDL 5.0
  1606. ;
  1607. ;  INTERNAL FUNCTIONS and PROCEDURES:
  1608. ;       pro demoroi$read_image     - Read JPEG image data
  1609. ;       pro demoroi$read_file_and_initialize
  1610. ;                                  - Read image and initialize the
  1611. ;                                    environment
  1612. ;       fun demoroi$histogram      - Define surface brightness
  1613. ;                                    distribution of objects
  1614. ;       pro demoroi$display_chosen_subset
  1615. ;                                  - Display objects selected from
  1616. ;                                    image
  1617. ;       pro demoroi$generate_gray_background
  1618. ;                                  - Create the "ghost" background
  1619. ;                                    grayscale image
  1620. ;       pro demoroi$generate_edge_enhanced_image
  1621. ;                                  - Create the edge-enhanced image
  1622. ;       pro demoroi$display_original_image
  1623. ;                                  - Display the image
  1624. ;       pro demoroi$find_edge_enhanced_contours
  1625. ;                                  - Contour the edge-enhanced image
  1626. ;       pro demoroi$find_objects   - Find objects based on edge
  1627. ;                                    enhancement, selection criteria
  1628. ;       pro demoroi$ghost_image_event
  1629. ;                                  - Turn on or off ghost image
  1630. ;       pro demoroi$apply_selection
  1631. ;                                  - Adjust selection criteria from
  1632. ;                                    widget settings
  1633. ;       pro demoroi$main_image_event
  1634. ;                                  - Take draw widget button events
  1635. ;                                    in manual mode
  1636. ;       pro demoroi_event          - Application main event handler
  1637. ;       fun demoroi$free_pointers  - Free heap variables that are
  1638. ;                                    no longer needed
  1639. ;       pro demoroi                - Application main procedure
  1640. ;
  1641. ;  EXTERNAL FUNCTIONS, PROCEDURES, and FILES:
  1642. ;       abell115.jpg               - Image for astronomy demo
  1643. ;       ???                        - Image for medical imaging demo
  1644. ;
  1645. ;  REFERENCE: IDL Reference Guide, IDL User's Guide
  1646. ;
  1647. ;  NAMED STRUCTURES:
  1648. ;       none.
  1649. ;
  1650. ;  COMMON BLOCS:
  1651. ;       none.
  1652. ;
  1653. ;  MODIFICATION HISTORY:
  1654. ;       3/97,   JLP   - Completed version for IDL 5.0.
  1655. ;-
  1656. ;--------------------------------------------------------------------
  1657. ;
  1658. ; If we already have an instance of this demo running,
  1659. ; then return.
  1660. ;
  1661.     IF (XREGISTERED('DemoROI', /NOSHOW)) THEN BEGIN
  1662.         V = DIALOG_MESSAGE('An instance of ROI is already running.')
  1663.         RETURN, PTR_NEW()
  1664.     ENDIF
  1665.  
  1666.     IF (N_ELEMENTS(Group_Leader) EQ 0) THEN Group_Leader = 0L
  1667.  
  1668.  
  1669.     IF (KEYWORD_SET(Astronomy_Demo) AND KEYWORD_SET(Medical_Demo)) $
  1670.         THEN BEGIN
  1671.         v = DIALOG_MESSAGE('/Astronomy_Demo and /Medical_Demo are ' + $
  1672.         'mutually exclusive.', /ERROR)
  1673.         RETURN, 0
  1674.     ENDIF
  1675.  
  1676.     ;  Get the screen size.
  1677.     ;
  1678.     Device, GET_SCREEN_SIZE=scrSize
  1679.  
  1680. ;
  1681. ;
  1682. ; Set up some text for the "About..." help.
  1683. ;
  1684. AstroDataText = [ $
  1685.    '', $
  1686.    '  This image is a composite of three optical CCD images', $
  1687.    '  taken with the 0.9 meter telescope at Kitt Peak National', $
  1688.    '  Observatory on Sept. 1, 1995.  The image shows Abell 115',  $
  1689.    '  a cluster of galaxies that is known for the very large, ', $
  1690.    '  bright elliptical cD galaxy near its center and its', $
  1691.    '  asymmetric X-ray emission.', $
  1692.    ' ', $
  1693.    '  Three images were taken of the central region of the ', $
  1694.    '  cluster,each through a different colored filter. Several ', $
  1695.    '  hundred galaxies have been detected and, by measuring their', $
  1696.    '  relative brightness in the three images, the data can help', $
  1697.    '  us determine how the galaxies in this cluster evolved.', $
  1698.    ' ', $
  1699.    '  These data are courtesy of Kathy Romer, Anne Metevier and', $
  1700.    '  Melville Ulmer (Northwestern University), with additional', $
  1701.    "  thanks to the Illinois Space Grant Consortium's ", $
  1702.    '  Northwestern University High School and Undergraduate ', $
  1703.    '  Research Program', $
  1704.    ' ']
  1705. AstroDemoText = [ $
  1706.    '', $
  1707.    "  This procedure illustrates IDL's ability to find", $
  1708.    '  "objects" in an image.  The user creates the definition', $
  1709.    '  of an object based on certain criteria, including the ', $
  1710.    '  "fuzziness" of the edge-enhanced image, and the size of', $
  1711.    '  the related contour.', $
  1712.    ' ', $
  1713.    '  In this example, the default values have been defined to', $
  1714.    '  allow us to search for small galaxies in the field of view.', $
  1715.    '  A galaxy will generally have a less well-defined border ', $
  1716.    '  than a star, so we look for objects with "fuzzy" edges.', $
  1717.    '  Specifying contour lengths also allows us to discriminate', $
  1718.    '  a range of perimeters of potential objects.', $
  1719.    ' ', $
  1720.    '  Object identification proceeds through three', $
  1721.    '  general steps:', $
  1722.    '        1) Edge enhancement,', $
  1723.    '        2) Contouring, and ', $
  1724.    '        3) Segmentation.', $
  1725.    ' ', $
  1726.    '  This procedure was authored by the Professional', $
  1727.    '  Services Group of Research Systems.  The source', $
  1728.    '  code to the demonstration routine can be customized', $
  1729.    '  to return information about individual objects.', $
  1730.    '  These data can in turn be fed into further', $
  1731.    '  processing and refinement steps.  For those desiring', $
  1732.    '  a larger implementation, the Professional Services', $
  1733.    '  Group can be contacted (psg@rsinc.com) for consulting', $
  1734.    '  and custom coding.']
  1735.  
  1736.     ; Save the system environment so we can restore it when the
  1737.     ; application completes.
  1738.     ;
  1739.     XSave = !X
  1740.     YSave = !Y
  1741.     ZSave = !Z
  1742.     PSave = !P
  1743.     TVLCT, OriginalR, OriginalG, OriginalB, /GET
  1744.  
  1745.  
  1746.     ; Set up the error trap.  Note that this only applies
  1747.     ; *before* XMANAGER is called since XMANAGER's CATCH will
  1748.     ; supercede this.
  1749.     ;
  1750.     IF (NOT KEYWORD_SET(No_Catch)) THEN BEGIN
  1751.         ErrorStatus = 0
  1752.         CATCH, ErrorStatus
  1753.         IF (ErrorStatus NE 0) THEN BEGIN
  1754. ;
  1755. ; We've caught an error.  Restore the system variables
  1756. ; and color table.
  1757. ;
  1758.             CATCH, /CANCEL
  1759.             !X = XSave
  1760.             !Y = YSave
  1761.             !Z = ZSave
  1762.             !P = PSave
  1763.             TVLCT, OriginalR, OriginalG, OriginalB
  1764. ;
  1765. ; Display as much information as we can about the error.
  1766. ;
  1767.       v = DIALOG_MESSAGE(['Unexpected error in ROI:', $
  1768.          '!ERR_STRING = ' + !ERR_STRING, $
  1769.          '!SYSERROR = ' + STRTRIM(LONG(!SYSERROR), 2), $
  1770.          '!SYSERR_STRING = ', !SYSERR_STRING, $
  1771.          ' ', 'Cleaning up...'], /ERROR)
  1772. ;
  1773. ; If the top level base is still around, destroy the
  1774. ; widgets.
  1775. ;
  1776.       IF (N_ELEMENTS(TLB) NE 0) THEN BEGIN
  1777.          IF (WIDGET_INFO(TLB, /VALID_ID)) THEN BEGIN
  1778.             WIDGET_CONTROL, TLB, /DESTROY
  1779.          ENDIF
  1780.       ENDIF
  1781. ;
  1782. ; Free any stray pointers which may exist.
  1783. ;
  1784.       Dummy = DemoROI$Free_Pointers(pDemo$State)
  1785.       PTR_FREE, Dummy
  1786.       HEAP_GC
  1787.       RETURN, -1
  1788.    ENDIF
  1789. ENDIF
  1790.  
  1791.  
  1792. ;
  1793. ; Set some flags and initial conditions.
  1794. ;
  1795. DemoMode = KEYWORD_SET(Astronomy_Demo) OR $
  1796.    KEYWORD_SET(Medical_Demo)
  1797. MapBases = DemoMode EQ 0
  1798. IF (KEYWORD_SET(Astronomy_Demo)) THEN BEGIN
  1799.    MaxCircumference = 300
  1800. ENDIF ELSE BEGIN
  1801.    MaxCircumference = 10000
  1802. ENDELSE
  1803.  
  1804.  
  1805. ;
  1806. ; Set up the application state structure.  Note that in
  1807. ; this demo version, not all structure elements correspond
  1808. ; to existing utilities!
  1809. ;
  1810. Demo$State = { $
  1811.    XSave                     : XSave, $
  1812.    YSave                     : YSave, $
  1813.    ZSave                     : ZSave, $
  1814.    PSave                     : PSave, $
  1815.    OriginalR                 : OriginalR, $
  1816.    OriginalG                 : OriginalG, $
  1817.    OriginalB                 : OriginalB, $
  1818.    Group_Leader              : Group_Leader, $
  1819.    pWidgets                  : PTR_NEW(/ALLOCATE_HEAP), $
  1820.    pTrueColorImage           : PTR_NEW(/ALLOCATE_HEAP), $
  1821.    pRGBImage                 : PTR_NEW(/ALLOCATE_HEAP), $
  1822.    pBWWeightedImage          : PTR_NEW(/ALLOCATE_HEAP), $
  1823.    pEdgeEnhancedImage        : PTR_NEW(/ALLOCATE_HEAP), $
  1824.    pHistogram                : PTR_NEW(/ALLOCATE_HEAP), $
  1825.    pObjects                  : PTR_NEW(/ALLOCATE_HEAP), $
  1826.    NoCatch                   : KEYWORD_SET(NO_CATCH), $
  1827.    No_Pointers               : KEYWORD_SET(NO_POINTERS), $
  1828.    Initialized               : 0B, $
  1829.    MinSobel                  : 20B, $
  1830.    MaxSobel                  : 255B, $
  1831.    MinCircumference          : 10, $
  1832.    MaxCircumference          : MaxCircumference/5., $
  1833.    Weights                   : [1, 1, 1], $
  1834.    HighLowContours           : 1B, $
  1835.    MinBrightness             : 1L, $
  1836.    MaxBrightness             : 765L, $
  1837.    ModifiedHighLow           : 0B, $
  1838.    MaxWindowDimension        : 512, $
  1839.    ImageWindow               : 0L, $
  1840.    ImagePixmap               : 0L, $
  1841.    HistoWindow               : 0L, $
  1842.    GrayBackgroundImagePixmap : 0L, $
  1843.    SubImagePixmap            : 0L, $
  1844.    SizeOfImage               : LONARR(6), $
  1845.    ScaleImagesToFit          : 0B, $
  1846.    ImageScrollBars           : 1B, $
  1847.    NReservedColors           : 5B, $
  1848.    BackgroundColor           : 0B, $
  1849.    GhostBackground           : 0B, $
  1850.    UseNegativeImage          : 1B, $
  1851.    ManualMode                : 0B, $
  1852.    DemoMode                  : DemoMode, $
  1853.    ManualCenter              : LONARR(2), $
  1854.    PreviousCenter            : LONARR(2), $
  1855.    ColorWhite                : (!D.N_COLORS LE 256)*!D.N_COLORS + $
  1856.                                (!D.N_COLORS GT 256)*'00FFFFFF'X, $
  1857.    AstroDataText             : AstroDataText, $
  1858.    AstroDemoText             : AstroDemoText $
  1859.    }
  1860.  
  1861.  
  1862.  
  1863. ;
  1864. ; Save the state structure to a heap variable.
  1865. ;
  1866. pDemo$State = PTR_NEW(Demo$State)
  1867.  
  1868.     ;
  1869.     ;  Set up the top level base widget.
  1870.     ;
  1871.     IF (KEYWORD_SET(Group_Leader)) THEN BEGIN
  1872.        TLB = WIDGET_BASE(/ROW, GROUP = Group_Leader, MBAR = MenuBar, $
  1873.           XPAD=0, YPAD=0, $
  1874.           TLB_FRAME_ATTR = 1, TITLE = 'Object Finder', FRAME = 2)
  1875.     ENDIF ELSE BEGIN
  1876.        TLB = WIDGET_BASE(/ROW, MBAR = MenuBar, TLB_FRAME_ATTR = 1, $
  1877.           XPAD=0, YPAD=0, $
  1878.           TITLE = 'Object Finder', FRAME = 2)
  1879.     ENDELSE
  1880.  
  1881.  
  1882.  
  1883. APPTLB = TLB
  1884.  
  1885. ;
  1886. ; There are different controls for "manual" and "automatic"
  1887. ; modes, so we use hidden bases to account for them.
  1888. ;
  1889. ControlBase = WIDGET_BASE(TLB)
  1890. ControlsBase = LONARR(3)
  1891.  
  1892.  
  1893. ;
  1894. ; The third control base is a dummy.  It's used to hide
  1895. ; some of the controls when we're running in demo mode.
  1896. ;
  1897. ControlsBase[2] = WIDGET_BASE(ControlBase, /COLUMN)
  1898.  
  1899.  
  1900. ;
  1901. ; The first control base holds the widgets for the
  1902. ; automatic mode.
  1903. ;
  1904. ControlsBase[0] = WIDGET_BASE(ControlBase, /COLUMN, /ALIGN_CENTER, $
  1905.    FRAME = 2)
  1906.  
  1907.  
  1908. ;
  1909. ; Define the slider for the fuzziness of edges for object
  1910. ; detection.  Rather than define exactly what the values
  1911. ; correspond to I simply use "Fuzzy" and "Distinct" as
  1912. ; labels for the slider.  These actually represent contour
  1913. ; levels of a Sobel image.
  1914. ;
  1915. SobelBase = WIDGET_BASE(ControlsBase[0], /COLUMN, /ALIGN_CENTER, $
  1916.    FRAME = 2)
  1917.     SobelLabel = WIDGET_LABEL(SobelBase, VALUE = '  Object Edges  ')
  1918.     SobelBase2 = WIDGET_BASE(SobelBase, /ROW)
  1919.     SobelLabel2 = WIDGET_LABEL(SobelBase2, VALUE = '  Fuzzy ')
  1920.     SobelSlider = WIDGET_SLIDER(SobelBase2, $
  1921.        VALUE = (*pDemo$State).MinSobel, MINIMUM = 1, MAXIMUM = 75, $
  1922.        /SUPPRESS)
  1923.     SobelLabel3 = WIDGET_LABEL(SobelBase2, VALUE = '  Distinct  ')
  1924.  
  1925.  
  1926. ;
  1927. ; Define sliders for the minimum and maximum perimeter (contour)
  1928. ; lengths that will be used to discriminate "objects".
  1929. ;
  1930. CircumferenceBase = WIDGET_BASE(ControlsBase[0], /COLUMN, $
  1931.    /ALIGN_CENTER, FRAME = 2)
  1932.     CircumferenceLabel = WIDGET_LABEL(CircumferenceBase, $
  1933.        VALUE = "  Objects' Circumference (pixels)  ")
  1934.     CircumferenceBase2 = WIDGET_BASE(CircumferenceBase, /ROW, $
  1935.        /ALIGN_CENTER)
  1936.     CircumferenceLabel2 = WIDGET_LABEL(CircumferenceBase2, $
  1937.        VALUE = 'Minimum  ')
  1938.     MinCircumferenceSlider = WIDGET_SLIDER(CircumferenceBase2, $
  1939.        VALUE = (*pDemo$State).MinCircumference, MINIMUM = 3, $
  1940.        MAXIMUM = MaxCircumference)
  1941.     CircumferenceBase3 = WIDGET_BASE(CircumferenceBase, /ROW, $
  1942.        /ALIGN_CENTER)
  1943.     CircumferenceLabel3 = WIDGET_LABEL(CircumferenceBase3, $
  1944.        VALUE = 'Maximum  ')
  1945.     MaxCircumferenceSlider = WIDGET_SLIDER(CircumferenceBase3, $
  1946.        VALUE = (*pDemo$State).MaxCircumference, MINIMUM = 3, $
  1947.        MAXIMUM = MaxCircumference)
  1948.  
  1949. ;
  1950. ; Define the R, G, and B sliders.  These are used to weight
  1951. ; the individual layers of the TrueColor image.  The default
  1952. ; is to weight each layer equally.
  1953. ;
  1954. IF (DemoMode) THEN BEGIN
  1955.    RGBSliderBase = WIDGET_BASE(ControlsBase[2])
  1956. ENDIF ELSE BEGIN
  1957.    RGBSliderBase = WIDGET_BASE(ControlsBase[0], /ALIGN_CENTER, $
  1958.       /COLUMN, FRAME = 2)
  1959. ENDELSE
  1960.  
  1961. RGBSliderLabel = WIDGET_LABEL(RGBSliderBase, $
  1962.    VALUE = '  Layer Weighting  ')
  1963. RGBSliderBase2 = WIDGET_BASE(RGBSliderBase, /ROW, /ALIGN_CENTER)
  1964. RGBSliderLabel2 = WIDGET_LABEL(RGBSliderBase2, VALUE = '  Red  ')
  1965. RSlider = WIDGET_SLIDER(RGBSliderBase2, $
  1966.    VALUE = (*pDemo$State).Weights[0], MINIMUM = 0, MAXIMUM = 10)
  1967. RGBSliderBase3 = WIDGET_BASE(RGBSliderBase, /ROW, /ALIGN_CENTER)
  1968. RGBSliderLabel3 = WIDGET_LABEL(RGBSliderBase3, VALUE = '  Green  ')
  1969. GSlider = WIDGET_SLIDER(RGBSliderBase3, $
  1970.    VALUE = (*pDemo$State).Weights[1], MINIMUM = 0, MAXIMUM = 10)
  1971. RGBSliderBase4 = WIDGET_BASE(RGBSliderBase, /ROW, /ALIGN_CENTER)
  1972. RGBSliderLabel4 = WIDGET_LABEL(RGBSliderBase4, VALUE = '  Blue  ')
  1973. BSlider = WIDGET_SLIDER(RGBSliderBase4, $
  1974.    VALUE = (*pDemo$State).Weights[2], MINIMUM = 0, MAXIMUM = 10)
  1975. ;
  1976. ; The IDL CONTOUR command returns both "high" and "low" contour
  1977. ; information.  In non-demo mode we allow the user to select
  1978. ; low, high, or both sets of contours when searching for objects.
  1979. ;
  1980. IF (DemoMode) THEN BEGIN
  1981.    ContoursBase = WIDGET_BASE(ControlsBase[2], /COLUMN)
  1982. ENDIF ELSE BEGIN
  1983.    ContoursBase = WIDGET_BASE(ControlsBase[0], /COLUMN, $
  1984.       /ALIGN_CENTER, FRAME = 2)
  1985. ENDELSE
  1986. ContoursLabel = WIDGET_LABEL(ContoursBase, $
  1987.    VALUE = '  Select Contours  ')
  1988. HighLowBase = WIDGET_BASE(ContoursBase, /ROW, /EXCLUSIVE)
  1989. AllButton = WIDGET_BUTTON(HighLowBase, VALUE = 'All', /NO_RELEASE)
  1990. HighButton = WIDGET_BUTTON(HighLowBase, VALUE = 'High', $
  1991.    /NO_RELEASE)
  1992. LowButton = WIDGET_BUTTON(HighLowBase, VALUE = 'Low', /NO_RELEASE)
  1993. WIDGET_CONTROL, HighButton, /SET_BUTTON
  1994. ;
  1995. ; In non-demo mode, we allow the user to further refine
  1996. ; object definition by surface brightness.
  1997. ;
  1998. IF (DemoMode) THEN BEGIN
  1999.    BrightnessBase = WIDGET_BASE(ControlsBase[2], /COLUMN)
  2000. ENDIF ELSE BEGIN
  2001.    BrightnessBase = WIDGET_BASE(ControlsBase[0], /COLUMN, $
  2002.       /ALIGN_CENTER, FRAME = 2)
  2003. ENDELSE
  2004. BrightnessLabel = WIDGET_LABEL(BrightnessBase, $
  2005.    VALUE = '  Brightness Limits  ')
  2006. BrightnessBase2 = WIDGET_BASE(BrightnessBase, /ROW)
  2007. BrightnessLabel2 = WIDGET_LABEL(BrightnessBase2, $
  2008.    VALUE = '  Minimum  ')
  2009. MinBrightnessSlider = WIDGET_SLIDER(BrightnessBase2, $
  2010.    VALUE = (*pDemo$State).MinBrightness, MINIMUM = 1, MAXIMUM = 255*3)
  2011. BrightnessBase3 = WIDGET_BASE(BrightnessBase, /ROW)
  2012. BrightnessLabel3 = WIDGET_LABEL(BrightnessBase3, $
  2013.    VALUE = '  Maximum  ')
  2014. MaxBrightnessSlider = WIDGET_SLIDER(BrightnessBase3, $
  2015.    VALUE = (*pDemo$State).MaxBrightness, MINIMUM = 1, MAXIMUM = 255*3)
  2016.  
  2017.   wApplyBase = WIDGET_BASE(ControlsBase[0], /ALIGN_CENTER,  /COLUMN )
  2018. ApplyButton = WIDGET_BUTTON(wApplyBAse, $
  2019.    VALUE = 'Find Objects')
  2020. TrueColorImageButton = WIDGET_BUTTON(wApplyBase, $
  2021.    VALUE = 'Display Original Image')
  2022.  
  2023.  
  2024. WIDGET_CONTROL, BrightnessBase, MAP = (NOT DemoMode)
  2025. ;
  2026. ; The "ghost" image is a darkened grayscale version of the
  2027. ; original image that can be used to underlay the objects.
  2028. ;
  2029. GhostBase = WIDGET_BASE(ControlsBase[0], /ROW, /ALIGN_CENTER, $
  2030.    EVENT_PRO = 'DemoROI$Ghost_Image_Event', FRAME = 2)
  2031. GhostLabel = WIDGET_LABEL(GhostBase, VALUE = ' Ghost Image ')
  2032. GhostButtonBase = WIDGET_BASE(GhostBase, /ROW, /ALIGN_CENTER, $
  2033.    /EXCLUSIVE)
  2034. GhostOnButton = WIDGET_BUTTON(GhostButtonBase, VALUE = 'On', $
  2035.    /NO_RELEASE)
  2036. GhostOffButton = WIDGET_BUTTON(GhostButtonBase, VALUE = 'Off', $
  2037.    /NO_RELEASE)
  2038. WIDGET_CONTROL, GhostOffButton, /SET_BUTTON
  2039. WIDGET_CONTROL, GhostBase, SENSITIVE = 0
  2040. IF (DemoMode) THEN BEGIN
  2041.    AstroStatusLabel = WIDGET_TEXT(ControlsBase[0], Value = '')
  2042. ENDIF ELSE BEGIN
  2043.    AstroStatusLabel = 0B
  2044. ENDELSE
  2045. ;
  2046. ; In non-demo mode, we also create a draw widget which will display
  2047. ; the distribution of objects by surface brightness as a histogram.
  2048. ;
  2049. IF (NOT DemoMode) THEN BEGIN
  2050.    Geom = WIDGET_INFO(ControlsBase[0], /GEOMETRY)
  2051.    HistoWindow = WIDGET_DRAW(ControlsBase[0], XSIZE = Geom.Scr_XSize, $
  2052.       YSIZE = Geom.Scr_XSize, RETAIN = 2, FRAME = 2)
  2053. ENDIF
  2054. ;
  2055. ; The second control base is used for "manual" mode.
  2056. ;
  2057. ControlsBase[1] = WIDGET_BASE(ControlBase, /COLUMN, /ALIGN_CENTER, $
  2058.    FRAME = 2)
  2059. InstructionsBase = WIDGET_BASE(ControlsBase[1], /COLUMN, $
  2060.    /ALIGN_CENTER, FRAME = 2)
  2061. v = WIDGET_LABEL(InstructionsBase, $
  2062.    VALUE = '  Select a point near the center  ', /ALIGN_LEFT)
  2063. v = WIDGET_LABEL(InstructionsBase, $
  2064.    VALUE = '  of an "object".  ', /ALIGN_LEFT)
  2065. ;
  2066. ; The "tightness" of a manual search is used internally as the threshold
  2067. ; level of SEARCH2D().
  2068. ;
  2069. TightnessBase = WIDGET_BASE(ControlsBase[1], /COLUMN, $
  2070.    /ALIGN_CENTER, FRAME = 2)
  2071. TightnessLabel = WIDGET_LABEL(TightnessBase, $
  2072.    VALUE = 'Search Pattern')
  2073. TightnessBase2 = WIDGET_BASE(TightnessBase, /ROW)
  2074. TightnessLabel2 = WIDGET_LABEL(TightnessBase2, VALUE = '  Loose  ')
  2075. TightnessSlider = WIDGET_SLIDER(TightnessBase2, VALUE = 235, $
  2076.    MINIMUM = 1, MAXIMUM = 255, /SUPPRESS)
  2077. TightnessLabel2 = WIDGET_LABEL(TightnessBase2, VALUE = '  Tight  ')
  2078. ApplyButton = WIDGET_BUTTON(TightnessBase, $
  2079.    VALUE = 'Apply Search Pattern')
  2080. TrueColorImageButton2 = WIDGET_BUTTON(ControlsBase[1], $
  2081.    VALUE = 'Display Original Image')
  2082. GhostBase2 = WIDGET_BASE(ControlsBase[1], /ROW, /ALIGN_CENTER, $
  2083.    EVENT_PRO = 'DemoROI$Ghost_Image_Event', FRAME = 2)
  2084. GhostLabel = WIDGET_LABEL(GhostBase2, VALUE = ' Ghost Image ')
  2085. GhostButtonBase = WIDGET_BASE(GhostBase2, /ROW, /ALIGN_CENTER, $
  2086.    /EXCLUSIVE)
  2087. GhostOnButton2 = WIDGET_BUTTON(GhostButtonBase, VALUE = 'On', $
  2088.    /NO_RELEASE)
  2089. GhostOffButton2 = WIDGET_BUTTON(GhostButtonBase, VALUE = 'Off', $
  2090.    /NO_RELEASE)
  2091. WIDGET_CONTROL, GhostOffButton2, /SET_BUTTON
  2092. NewListButton = WIDGET_BUTTON(ControlsBase[1], $
  2093.    VALUE = 'Start A New List Of Objects')
  2094. ;
  2095. ; Define the "System" menus which reside across the top
  2096. ; of the base widget.
  2097. ;
  2098. FileMenu = WIDGET_BUTTON(MenuBar, VALUE = 'File', /MENU)
  2099. ;
  2100. ; In non-demo mode, we allow the user to open files.  In
  2101. ; demo mode, they're restricted.
  2102. ;
  2103. IF (DemoMode) THEN BEGIN
  2104.    IF (KEYWORD_SET(Astronomy_Demo)) THEN BEGIN
  2105.       AstronomyButton = WIDGET_BUTTON(FileMenu, $
  2106.          VALUE = 'Astronomical Data')
  2107.       WIDGET_CONTROL, AstronomyButton, SENSITIVE = 0
  2108.    ENDIF ELSE BEGIN
  2109.       MedicalButton = WIDGET_BUTTON(FileMenu, $
  2110.          VALUE = 'Medical Imaging Data')
  2111.       WIDGET_CONTROL, MedicalButton, SENSITIVE = 0
  2112.    ENDELSE
  2113. ENDIF ELSE BEGIN
  2114.    OpenButton = WIDGET_BUTTON(FileMenu, VALUE = 'Open...')
  2115. ENDELSE
  2116. ExitButton = WIDGET_BUTTON(FileMenu, VALUE = 'Quit', /SEPARATOR)
  2117. ;
  2118. ; The "Search" menu allows the user to switch between automatic
  2119. ; and manual modes (though this might not always make sense.)
  2120. ;
  2121. IF (NOT DemoMode) THEN BEGIN
  2122.    SearchMenu = WIDGET_BUTTON(MenuBar, $
  2123.       VALUE = 'Object Search Method', /MENU)
  2124.    AutomaticButton = WIDGET_BUTTON(SearchMenu, VALUE = 'Automatic')
  2125.    ManualButton = WIDGET_BUTTON(SearchMenu, VALUE = 'Manual')
  2126. ;
  2127. ; In demo mode, only allow the mode appropriate for the
  2128. ; specific demo.
  2129. ;
  2130.    IF (KEYWORD_SET(ASTRONOMY_DEMO)) THEN BEGIN
  2131.       WIDGET_CONTROL, ManualButton, SENSITIVE = 0
  2132.    ENDIF
  2133.    IF (KEYWORD_SET(MEDICAL_DEMO)) THEN BEGIN
  2134.       WIDGET_CONTROL, AutomaticButton, SENSITIVE = 0
  2135.    ENDIF
  2136. ENDIF ELSE BEGIN
  2137.    AutomaticButton = 0L
  2138.    ManualButton = 0L
  2139. ENDELSE
  2140. ;
  2141. ; In demo mode, add some "about..." help buttons.
  2142. ;
  2143. IF (DemoMode) THEN BEGIN
  2144.    HelpMenu = WIDGET_BUTTON(MenuBar, /HELP,  VALUE = 'About', /MENU)
  2145. ;   HelpMenu = WIDGET_BUTTON(MenuBar, /HELP,  VALUE = 'Help', /MENU)
  2146.    AboutDataButton = WIDGET_BUTTON(HelpMenu, VALUE = $
  2147.       'About the data...')
  2148.    AboutDemoButton = WIDGET_BUTTON(HelpMenu, VALUE = $
  2149.       'About the demo...')
  2150. ENDIF
  2151. ;
  2152. ; Create a base next to the controls.  This will contain
  2153. ; the draw widget for the image.
  2154. ;
  2155. ImageBase = WIDGET_BASE(TLB, /COLUMN, FRAME = 2, $
  2156.    EVENT_PRO = 'DemoROI$Main_Image_Event')
  2157. DrawWidget = WIDGET_DRAW(ImageBase, RETAIN = 2)
  2158. ;
  2159. ; Create a structure that contains the IDs of the widgets
  2160. ; we will want to map, unmap, poll, set, etc.
  2161. ;
  2162. WidgetIDs = { $
  2163.    ControlsBase           : ControlsBase, $
  2164.    SobelSlider            : SobelSlider, $
  2165.    MinCircumferenceSlider : MinCircumferenceSlider, $
  2166.    MaxCircumferenceSlider : MaxCircumferenceSlider, $
  2167.    RedSlider              : RSlider, $
  2168.    GreenSlider            : GSlider, $
  2169.    BlueSlider             : BSlider, $
  2170.    MinBrightnessSlider    : MinBrightnessSlider, $
  2171.    MaxBrightnessSlider    : MaxBrightnessSlider, $
  2172.    GhostBase              : GhostBase, $
  2173.    GhostOnButton          : GhostOnButton, $
  2174.    GhostOffButton         : GhostOffButton, $
  2175.    GhostOnButton2         : GhostOnButton2, $
  2176.    GhostOffButton2        : GhostOffButton2, $
  2177.    TightnessSlider        : TightnessSlider, $
  2178.    ImageBase              : ImageBase, $
  2179.    AutomaticButton        : AutomaticButton, $
  2180.    ManualButton           : ManualButton, $
  2181.    AstroStatusLabel       : AstroStatusLabel, $
  2182.    DrawWidget             : DrawWidget $
  2183.    }
  2184. ;
  2185. ; Store the widget structure into the application state
  2186. ; structure.
  2187. ;
  2188. *(*pDemo$State).pWidgets = WidgetIDs
  2189. ;
  2190. ; Map and unmap (hide) the control bases appropriate
  2191. ; to the mode in which the application is being run.
  2192. ;
  2193. IF (NOT KEYWORD_SET(Medical_Demo)) THEN BEGIN
  2194.    WIDGET_CONTROL, ControlsBase[0], MAP = 1
  2195.    FOR I = 1, N_ELEMENTS(ControlsBase) - 1 DO BEGIN
  2196.       WIDGET_CONTROL, ControlsBase[I], MAP = 0
  2197.    ENDFOR
  2198. ENDIF ELSE BEGIN
  2199.    WIDGET_CONTROL, ControlsBase[0], MAP = 0
  2200.    WIDGET_CONTROL, ControlsBase[1], MAP = 1
  2201.    FOR I = 2, N_ELEMENTS(ControlsBase) - 1 DO BEGIN
  2202.       WIDGET_CONTROL, ControlsBase[I], MAP = 0
  2203.    ENDFOR
  2204.    (*pDemo$State).ManualMode = 1
  2205. ENDELSE
  2206. ;
  2207. ; Realize the widget.
  2208. ;
  2209. WIDGET_CONTROL, TLB, /REALIZE
  2210. ;
  2211. ; Get the ID of the histogram window, if we're not
  2212. ; in demo mode.
  2213. ;
  2214. IF (NOT DemoMode) THEN BEGIN
  2215.    WIDGET_CONTROL, HistoWindow, GET_VALUE = WindowID
  2216.    (*pDemo$State).HistoWindow = WindowID
  2217. ENDIF
  2218. ;
  2219. ; Set the UVALUE of the top level base to the
  2220. ; pointer to the state structure heap variable.
  2221. ;
  2222. WIDGET_CONTROL, TLB, SET_UVALUE = pDemo$State
  2223. ;
  2224. ; In demo mode, we have specific files to be read.
  2225. ;
  2226. IF (DemoMode) THEN BEGIN
  2227.    IF (KEYWORD_SET(Astronomy_Demo)) THEN BEGIN
  2228.       WIDGET_CONTROL, AstroStatusLabel, SET_VALUE = $
  2229.          'Loading image...'
  2230.       DemoROI$Read_File_And_Initialize, pDemo$State, $
  2231.          FILE = FILEPATH('abell115.jpg', $
  2232.          SUBDIR = ['examples', 'demo', 'demodata'])
  2233.       WIDGET_CONTROL, AstroStatusLabel, SET_VALUE = ''
  2234.    ENDIF ELSE BEGIN
  2235.       DemoROI$Read_File_And_Initialize, pDemo$State, $
  2236.          FILE = FILEPATH('pollens.jpg', $
  2237.          SUBDIR = ['examples', 'demo', 'demodata'])
  2238.    ENDELSE
  2239. ENDIF
  2240.  
  2241. ;
  2242. ; Intercept kill requests to force the user to exit
  2243. ; gracefully via File/Exit.
  2244. ;
  2245. WIDGET_CONTROL, TLB, /TLB_KILL_REQUEST_EVENTS
  2246.  
  2247. CATCH, /CANCEL
  2248. ;
  2249. ; Start up the event handler.
  2250. ; This Xmanager call may not block if being called from
  2251. ; another application which does not block
  2252. ;
  2253. XMANAGER, 'DemoROI', TLB, EVENT_HANDLER = 'DemoROI_Event', $
  2254.           CLEANUP='demoROI_Cleanup'
  2255. ;
  2256. ; We're back from the event loop if we were in blocking mode,
  2257. ; or the application has just been launched if in non-blocking mode
  2258.  
  2259. IF (NOT DemoMode) THEN BEGIN
  2260.  
  2261.    ; Free all the pointers associated with the application,
  2262.    ; but keep any pointers to the objects.
  2263.    ;
  2264.    pObjects = DemoROI$Free_Pointers(pDemo$State, /Keep_Objects)
  2265.  
  2266. ENDIF ELSE pObjects = PTR_NEW()
  2267.  
  2268. ;
  2269. ; pObjects will be a NULL pointer if we're not returning
  2270. ; object information.
  2271. ;
  2272. RETURN, pObjects
  2273. END
  2274.  
  2275.  
  2276. Pro d_ROI, AppTLB = AppTLB, _Extra = Extra
  2277. ;
  2278. ; This is the IDL Demo's entry point for accessing
  2279. ; the DemoROI function.
  2280. ;
  2281. Dummy = DemoROI(_Extra = Extra, APPTLB = AppTLB)
  2282. End
  2283.